From e6918187568dbd01842d8d1d2c808ce16a894239 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 21 Apr 2024 13:54:28 +0200 Subject: Adding upstream version 18.2.2. Signed-off-by: Daniel Baumann --- src/jaegertracing/opentelemetry-cpp/sdk/BUILD | 7 + .../opentelemetry-cpp/sdk/CHANGELOG.md | 27 + .../opentelemetry-cpp/sdk/CMakeLists.txt | 44 ++ .../sdk/_metrics/aggregator/aggregator.h | 156 ++++ .../sdk/_metrics/aggregator/counter_aggregator.h | 108 +++ .../sdk/_metrics/aggregator/exact_aggregator.h | 169 +++++ .../sdk/_metrics/aggregator/gauge_aggregator.h | 146 ++++ .../sdk/_metrics/aggregator/histogram_aggregator.h | 207 ++++++ .../aggregator/min_max_sum_count_aggregator.h | 159 +++++ .../sdk/_metrics/aggregator/sketch_aggregator.h | 282 ++++++++ .../opentelemetry/sdk/_metrics/async_instruments.h | 286 ++++++++ .../opentelemetry/sdk/_metrics/controller.h | 154 ++++ .../include/opentelemetry/sdk/_metrics/exporter.h | 35 + .../opentelemetry/sdk/_metrics/instrument.h | 312 ++++++++ .../sdk/include/opentelemetry/sdk/_metrics/meter.h | 392 +++++++++++ .../opentelemetry/sdk/_metrics/meter_provider.h | 37 + .../include/opentelemetry/sdk/_metrics/processor.h | 38 + .../include/opentelemetry/sdk/_metrics/record.h | 50 ++ .../opentelemetry/sdk/_metrics/sync_instruments.h | 465 ++++++++++++ .../sdk/_metrics/ungrouped_processor.h | 365 ++++++++++ .../opentelemetry/sdk/common/atomic_shared_ptr.h | 62 ++ .../opentelemetry/sdk/common/atomic_unique_ptr.h | 87 +++ .../opentelemetry/sdk/common/attribute_utils.h | 199 ++++++ .../opentelemetry/sdk/common/attributemap_hash.h | 62 ++ .../opentelemetry/sdk/common/circular_buffer.h | 186 +++++ .../sdk/common/circular_buffer_range.h | 93 +++ .../opentelemetry/sdk/common/empty_attributes.h | 34 + .../opentelemetry/sdk/common/env_variables.h | 53 ++ .../opentelemetry/sdk/common/exporter_utils.h | 34 + .../opentelemetry/sdk/common/global_log_handler.h | 222 ++++++ .../instrumentation_library.h | 96 +++ .../opentelemetry/sdk/logs/batch_log_processor.h | 128 ++++ .../sdk/include/opentelemetry/sdk/logs/exporter.h | 62 ++ .../include/opentelemetry/sdk/logs/log_record.h | 193 +++++ .../sdk/include/opentelemetry/sdk/logs/logger.h | 77 ++ .../opentelemetry/sdk/logs/logger_context.h | 81 +++ .../opentelemetry/sdk/logs/logger_provider.h | 131 ++++ .../opentelemetry/sdk/logs/multi_log_processor.h | 69 ++ .../opentelemetry/sdk/logs/multi_recordable.h | 104 +++ .../sdk/include/opentelemetry/sdk/logs/processor.h | 61 ++ .../include/opentelemetry/sdk/logs/recordable.h | 97 +++ .../opentelemetry/sdk/logs/simple_log_processor.h | 55 ++ .../sdk/metrics/aggregation/aggregation.h | 55 ++ .../sdk/metrics/aggregation/default_aggregation.h | 146 ++++ .../sdk/metrics/aggregation/drop_aggregation.h | 51 ++ .../metrics/aggregation/histogram_aggregation.h | 103 +++ .../metrics/aggregation/lastvalue_aggregation.h | 63 ++ .../sdk/metrics/aggregation/sum_aggregation.h | 64 ++ .../opentelemetry/sdk/metrics/async_instruments.h | 115 +++ .../opentelemetry/sdk/metrics/data/metric_data.h | 42 ++ .../opentelemetry/sdk/metrics/data/point_data.h | 78 ++ .../sdk/metrics/exemplar/always_sample_filter.h | 43 ++ .../opentelemetry/sdk/metrics/exemplar/data.h | 45 ++ .../opentelemetry/sdk/metrics/exemplar/filter.h | 39 + .../sdk/metrics/exemplar/never_sample_filter.h | 43 ++ .../sdk/metrics/exemplar/no_exemplar_reservoir.h | 55 ++ .../opentelemetry/sdk/metrics/exemplar/reservoir.h | 55 ++ .../sdk/metrics/export/metric_producer.h | 57 ++ .../export/periodic_exporting_metric_reader.h | 72 ++ .../opentelemetry/sdk/metrics/instruments.h | 70 ++ .../sdk/include/opentelemetry/sdk/metrics/meter.h | 156 ++++ .../opentelemetry/sdk/metrics/meter_context.h | 133 ++++ .../opentelemetry/sdk/metrics/meter_provider.h | 95 +++ .../opentelemetry/sdk/metrics/metric_exporter.h | 55 ++ .../opentelemetry/sdk/metrics/metric_reader.h | 72 ++ .../opentelemetry/sdk/metrics/observer_result.h | 48 ++ .../sdk/metrics/state/async_metric_storage.h | 95 +++ .../sdk/metrics/state/attributes_hashmap.h | 124 ++++ .../sdk/metrics/state/metric_collector.h | 57 ++ .../sdk/metrics/state/metric_storage.h | 86 +++ .../sdk/metrics/state/multi_metric_storage.h | 68 ++ .../sdk/metrics/state/sync_metric_storage.h | 119 ++++ .../sdk/metrics/state/temporal_metric_storage.h | 50 ++ .../opentelemetry/sdk/metrics/sync_instruments.h | 127 ++++ .../sdk/metrics/view/attributes_processor.h | 81 +++ .../sdk/metrics/view/instrument_selector.h | 36 + .../sdk/metrics/view/meter_selector.h | 47 ++ .../opentelemetry/sdk/metrics/view/predicate.h | 82 +++ .../sdk/metrics/view/predicate_factory.h | 45 ++ .../include/opentelemetry/sdk/metrics/view/view.h | 57 ++ .../opentelemetry/sdk/metrics/view/view_registry.h | 102 +++ .../resource/experimental_semantic_conventions.h | 141 ++++ .../include/opentelemetry/sdk/resource/resource.h | 86 +++ .../opentelemetry/sdk/resource/resource_detector.h | 38 + .../opentelemetry/sdk/trace/batch_span_processor.h | 158 +++++ .../sdk/include/opentelemetry/sdk/trace/exporter.h | 55 ++ .../include/opentelemetry/sdk/trace/id_generator.h | 31 + .../opentelemetry/sdk/trace/multi_recordable.h | 161 +++++ .../opentelemetry/sdk/trace/multi_span_processor.h | 187 +++++ .../include/opentelemetry/sdk/trace/processor.h | 70 ++ .../opentelemetry/sdk/trace/random_id_generator.h | 24 + .../include/opentelemetry/sdk/trace/recordable.h | 152 ++++ .../sdk/include/opentelemetry/sdk/trace/sampler.h | 91 +++ .../opentelemetry/sdk/trace/samplers/always_off.h | 48 ++ .../opentelemetry/sdk/trace/samplers/always_on.h | 47 ++ .../opentelemetry/sdk/trace/samplers/parent.h | 45 ++ .../sdk/trace/samplers/trace_id_ratio.h | 53 ++ .../opentelemetry/sdk/trace/simple_processor.h | 84 +++ .../include/opentelemetry/sdk/trace/span_data.h | 304 ++++++++ .../sdk/include/opentelemetry/sdk/trace/tracer.h | 70 ++ .../opentelemetry/sdk/trace/tracer_context.h | 100 +++ .../opentelemetry/sdk/trace/tracer_provider.h | 103 +++ .../include/opentelemetry/sdk/version/version.h | 30 + .../sdk/include/opentelemetry/sdk_config.h | 7 + .../opentelemetry-cpp/sdk/src/CMakeLists.txt | 12 + .../opentelemetry-cpp/sdk/src/_metrics/BUILD | 26 + .../sdk/src/_metrics/CMakeLists.txt | 19 + .../opentelemetry-cpp/sdk/src/_metrics/meter.cc | 782 +++++++++++++++++++++ .../sdk/src/_metrics/meter_provider.cc | 29 + .../sdk/src/_metrics/ungrouped_processor.cc | 178 +++++ .../opentelemetry-cpp/sdk/src/common/BUILD | 44 ++ .../sdk/src/common/CMakeLists.txt | 21 + .../opentelemetry-cpp/sdk/src/common/core.cc | 8 + .../sdk/src/common/fast_random_number_generator.h | 82 +++ .../sdk/src/common/global_log_handler.cc | 57 ++ .../sdk/src/common/platform/BUILD | 34 + .../sdk/src/common/platform/fork.h | 24 + .../sdk/src/common/platform/fork_unix.cc | 22 + .../sdk/src/common/platform/fork_windows.cc | 23 + .../opentelemetry-cpp/sdk/src/common/random.cc | 80 +++ .../opentelemetry-cpp/sdk/src/common/random.h | 41 ++ .../opentelemetry-cpp/sdk/src/logs/BUILD | 28 + .../opentelemetry-cpp/sdk/src/logs/CMakeLists.txt | 25 + .../sdk/src/logs/batch_log_processor.cc | 208 ++++++ .../opentelemetry-cpp/sdk/src/logs/logger.cc | 126 ++++ .../sdk/src/logs/logger_context.cc | 54 ++ .../sdk/src/logs/logger_provider.cc | 142 ++++ .../sdk/src/logs/multi_log_processor.cc | 151 ++++ .../sdk/src/logs/multi_recordable.cc | 140 ++++ .../sdk/src/logs/simple_log_processor.cc | 64 ++ .../opentelemetry-cpp/sdk/src/metrics/BUILD | 28 + .../sdk/src/metrics/CMakeLists.txt | 29 + .../metrics/aggregation/histogram_aggregation.cc | 142 ++++ .../metrics/aggregation/lastvalue_aggregation.cc | 134 ++++ .../sdk/src/metrics/aggregation/sum_aggregation.cc | 111 +++ .../export/periodic_exporting_metric_reader.cc | 101 +++ .../opentelemetry-cpp/sdk/src/metrics/meter.cc | 259 +++++++ .../sdk/src/metrics/meter_context.cc | 108 +++ .../sdk/src/metrics/meter_provider.cc | 94 +++ .../sdk/src/metrics/metric_reader.cc | 94 +++ .../sdk/src/metrics/state/metric_collector.cc | 62 ++ .../sdk/src/metrics/state/sync_metric_storage.cc | 36 + .../src/metrics/state/temporal_metric_storage.cc | 131 ++++ .../sdk/src/metrics/sync_instruments.cc | 202 ++++++ .../opentelemetry-cpp/sdk/src/resource/BUILD | 26 + .../sdk/src/resource/CMakeLists.txt | 16 + .../opentelemetry-cpp/sdk/src/resource/resource.cc | 82 +++ .../sdk/src/resource/resource_detector.cc | 39 + .../opentelemetry-cpp/sdk/src/trace/BUILD | 29 + .../opentelemetry-cpp/sdk/src/trace/CMakeLists.txt | 26 + .../sdk/src/trace/batch_span_processor.cc | 211 ++++++ .../sdk/src/trace/random_id_generator.cc | 30 + .../sdk/src/trace/samplers/parent.cc | 48 ++ .../sdk/src/trace/samplers/trace_id_ratio.cc | 111 +++ .../opentelemetry-cpp/sdk/src/trace/span.cc | 187 +++++ .../opentelemetry-cpp/sdk/src/trace/span.h | 66 ++ .../opentelemetry-cpp/sdk/src/trace/tracer.cc | 116 +++ .../sdk/src/trace/tracer_context.cc | 63 ++ .../sdk/src/trace/tracer_provider.cc | 104 +++ .../sdk/src/version/CMakeLists.txt | 17 + .../opentelemetry-cpp/sdk/src/version/version.cc | 24 + .../opentelemetry-cpp/sdk/test/CMakeLists.txt | 12 + .../opentelemetry-cpp/sdk/test/_metrics/BUILD | 165 +++++ .../sdk/test/_metrics/CMakeLists.txt | 21 + .../sdk/test/_metrics/controller_test.cc | 56 ++ .../sdk/test/_metrics/counter_aggregator_test.cc | 120 ++++ .../sdk/test/_metrics/exact_aggregator_test.cc | 229 ++++++ .../sdk/test/_metrics/gauge_aggregator_test.cc | 133 ++++ .../sdk/test/_metrics/histogram_aggregator_test.cc | 173 +++++ .../sdk/test/_metrics/meter_provider_sdk_test.cc | 26 + .../sdk/test/_metrics/meter_test.cc | 297 ++++++++ .../sdk/test/_metrics/metric_instrument_test.cc | 487 +++++++++++++ .../_metrics/min_max_sum_count_aggregator_test.cc | 209 ++++++ .../sdk/test/_metrics/sketch_aggregator_test.cc | 254 +++++++ .../sdk/test/_metrics/ungrouped_processor_test.cc | 601 ++++++++++++++++ .../opentelemetry-cpp/sdk/test/common/BUILD | 153 ++++ .../sdk/test/common/CMakeLists.txt | 37 + .../sdk/test/common/atomic_unique_ptr_test.cc | 42 ++ .../sdk/test/common/attribute_utils_test.cc | 53 ++ .../sdk/test/common/attributemap_hash_benchmark.cc | 22 + .../sdk/test/common/attributemap_hash_test.cc | 32 + .../sdk/test/common/baseline_circular_buffer.h | 88 +++ .../sdk/test/common/circular_buffer_benchmark.cc | 133 ++++ .../sdk/test/common/circular_buffer_range_test.cc | 59 ++ .../sdk/test/common/circular_buffer_test.cc | 161 +++++ .../sdk/test/common/empty_attributes_test.cc | 19 + .../common/fast_random_number_generator_test.cc | 22 + .../sdk/test/common/global_log_handle_test.cc | 67 ++ .../sdk/test/common/random_benchmark.cc | 35 + .../sdk/test/common/random_fork_test.cc | 53 ++ .../sdk/test/common/random_test.cc | 35 + .../sdk/test/instrumentationlibrary/BUILD | 14 + .../sdk/test/instrumentationlibrary/CMakeLists.txt | 11 + .../instrumentationlibrary_test.cc | 26 + .../opentelemetry-cpp/sdk/test/logs/BUILD | 75 ++ .../opentelemetry-cpp/sdk/test/logs/CMakeLists.txt | 10 + .../sdk/test/logs/batch_log_processor_test.cc | 269 +++++++ .../sdk/test/logs/log_record_test.cc | 66 ++ .../sdk/test/logs/logger_provider_sdk_test.cc | 133 ++++ .../sdk/test/logs/logger_sdk_test.cc | 97 +++ .../sdk/test/logs/simple_log_processor_test.cc | 152 ++++ .../opentelemetry-cpp/sdk/test/metrics/BUILD | 203 ++++++ .../sdk/test/metrics/CMakeLists.txt | 34 + .../sdk/test/metrics/aggregation_test.cc | 182 +++++ .../sdk/test/metrics/async_instruments_test.cc | 60 ++ .../sdk/test/metrics/async_metric_storage_test.cc | 132 ++++ .../test/metrics/attributes_hashmap_benchmark.cc | 53 ++ .../sdk/test/metrics/attributes_hashmap_test.cc | 71 ++ .../test/metrics/attributes_processor_benchmark.cc | 27 + .../sdk/test/metrics/attributes_processor_test.cc | 49 ++ .../sdk/test/metrics/exemplar/BUILD | 47 ++ .../sdk/test/metrics/exemplar/CMakeLists.txt | 10 + .../metrics/exemplar/always_sample_filter_test.cc | 19 + .../metrics/exemplar/never_sample_filter_test.cc | 20 + .../metrics/exemplar/no_exemplar_reservoir_test.cc | 22 + .../sdk/test/metrics/meter_provider_sdk_test.cc | 91 +++ .../sdk/test/metrics/metric_reader_test.cc | 39 + .../sdk/test/metrics/multi_metric_storage_test.cc | 62 ++ .../sdk/test/metrics/observer_result_test.cc | 38 + .../periodic_exporting_metric_reader_test.cc | 81 +++ .../sdk/test/metrics/sync_instruments_test.cc | 138 ++++ .../sdk/test/metrics/sync_metric_storage_test.cc | 246 +++++++ .../sdk/test/metrics/view_registry_test.cc | 82 +++ .../opentelemetry-cpp/sdk/test/resource/BUILD | 12 + .../sdk/test/resource/CMakeLists.txt | 9 + .../sdk/test/resource/resource_test.cc | 213 ++++++ .../opentelemetry-cpp/sdk/test/trace/BUILD | 155 ++++ .../sdk/test/trace/CMakeLists.txt | 30 + .../sdk/test/trace/always_off_sampler_test.cc | 42 ++ .../sdk/test/trace/always_on_sampler_test.cc | 60 ++ .../sdk/test/trace/batch_span_processor_test.cc | 291 ++++++++ .../sdk/test/trace/parent_sampler_test.cc | 73 ++ .../sdk/test/trace/sampler_benchmark.cc | 156 ++++ .../sdk/test/trace/simple_processor_test.cc | 75 ++ .../sdk/test/trace/span_data_test.cc | 135 ++++ .../sdk/test/trace/trace_id_ratio_sampler_test.cc | 258 +++++++ .../sdk/test/trace/tracer_provider_test.cc | 99 +++ .../sdk/test/trace/tracer_test.cc | 731 +++++++++++++++++++ 238 files changed, 24479 insertions(+) create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/BUILD create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/CHANGELOG.md create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/aggregator.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/counter_aggregator.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/exact_aggregator.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/gauge_aggregator.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/histogram_aggregator.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/min_max_sum_count_aggregator.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/sketch_aggregator.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/async_instruments.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/controller.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/exporter.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/instrument.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/meter.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/meter_provider.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/processor.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/record.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/sync_instruments.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/ungrouped_processor.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/atomic_shared_ptr.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/atomic_unique_ptr.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/attribute_utils.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/attributemap_hash.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/circular_buffer.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/circular_buffer_range.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/empty_attributes.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/env_variables.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/exporter_utils.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/global_log_handler.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/batch_log_processor.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/exporter.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/log_record.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/logger.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/logger_context.h create mode 100755 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/logger_provider.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/multi_log_processor.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/multi_recordable.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/processor.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/recordable.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/simple_log_processor.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/aggregation.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/default_aggregation.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/drop_aggregation.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/lastvalue_aggregation.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/sum_aggregation.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/async_instruments.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/data/metric_data.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/data/point_data.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/always_sample_filter.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/data.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/filter.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/never_sample_filter.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/export/metric_producer.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/instruments.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/meter.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/meter_context.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/meter_provider.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/metric_exporter.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/metric_reader.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/observer_result.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/async_metric_storage.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/attributes_hashmap.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/metric_collector.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/metric_storage.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/multi_metric_storage.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/temporal_metric_storage.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/sync_instruments.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/attributes_processor.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/instrument_selector.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/meter_selector.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/predicate.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/predicate_factory.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/view.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/view_registry.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/resource/experimental_semantic_conventions.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/resource/resource.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/resource/resource_detector.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/batch_span_processor.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/exporter.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/id_generator.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/multi_recordable.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/multi_span_processor.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/processor.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/random_id_generator.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/recordable.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/sampler.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/samplers/always_off.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/samplers/always_on.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/samplers/parent.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/samplers/trace_id_ratio.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/simple_processor.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/span_data.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/tracer.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/tracer_context.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/tracer_provider.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/version/version.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk_config.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/BUILD create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/meter.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/meter_provider.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/ungrouped_processor.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/common/BUILD create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/common/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/common/core.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/common/fast_random_number_generator.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/common/global_log_handler.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/common/platform/BUILD create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/common/platform/fork.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/common/platform/fork_unix.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/common/platform/fork_windows.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/common/random.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/common/random.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/logs/BUILD create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/logs/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/logs/batch_log_processor.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/logs/logger.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/logs/logger_context.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/logs/logger_provider.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/logs/multi_log_processor.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/logs/multi_recordable.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/logs/simple_log_processor.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/BUILD create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/aggregation/histogram_aggregation.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/aggregation/lastvalue_aggregation.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/aggregation/sum_aggregation.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/export/periodic_exporting_metric_reader.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/meter.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/meter_context.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/meter_provider.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/metric_reader.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/state/metric_collector.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/state/sync_metric_storage.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/state/temporal_metric_storage.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/sync_instruments.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/resource/BUILD create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/resource/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/resource/resource.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/resource/resource_detector.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/trace/BUILD create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/trace/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/trace/batch_span_processor.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/trace/random_id_generator.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/trace/samplers/parent.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/trace/samplers/trace_id_ratio.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/trace/span.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/trace/span.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/trace/tracer.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/trace/tracer_context.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/trace/tracer_provider.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/version/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/src/version/version.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/BUILD create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/controller_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/counter_aggregator_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/exact_aggregator_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/gauge_aggregator_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/histogram_aggregator_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/meter_provider_sdk_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/meter_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/metric_instrument_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/min_max_sum_count_aggregator_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/sketch_aggregator_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/ungrouped_processor_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/common/BUILD create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/common/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/common/atomic_unique_ptr_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/common/attribute_utils_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/common/attributemap_hash_benchmark.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/common/attributemap_hash_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/common/baseline_circular_buffer.h create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_benchmark.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_range_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/common/empty_attributes_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/common/fast_random_number_generator_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/common/global_log_handle_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_benchmark.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_fork_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/BUILD create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/instrumentationlibrary_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/logs/BUILD create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/logs/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/logs/batch_log_processor_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/logs/log_record_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_provider_sdk_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_sdk_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/logs/simple_log_processor_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/BUILD create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/aggregation_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/async_instruments_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/async_metric_storage_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_hashmap_benchmark.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_hashmap_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_processor_benchmark.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_processor_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/BUILD create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/always_sample_filter_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/never_sample_filter_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/no_exemplar_reservoir_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/meter_provider_sdk_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/metric_reader_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/multi_metric_storage_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/observer_result_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/periodic_exporting_metric_reader_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/sync_instruments_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/sync_metric_storage_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/view_registry_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/resource/BUILD create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/resource/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/resource/resource_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/trace/BUILD create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/trace/CMakeLists.txt create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/trace/always_off_sampler_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/trace/always_on_sampler_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/trace/batch_span_processor_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/trace/parent_sampler_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/trace/sampler_benchmark.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/trace/simple_processor_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/trace/span_data_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/trace/trace_id_ratio_sampler_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/trace/tracer_provider_test.cc create mode 100644 src/jaegertracing/opentelemetry-cpp/sdk/test/trace/tracer_test.cc (limited to 'src/jaegertracing/opentelemetry-cpp/sdk') diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/BUILD new file mode 100644 index 000000000..cc62431b5 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/BUILD @@ -0,0 +1,7 @@ +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "headers", + hdrs = glob(["include/**/*.h"]), + strip_include_prefix = "include", +) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/CHANGELOG.md b/src/jaegertracing/opentelemetry-cpp/sdk/CHANGELOG.md new file mode 100644 index 000000000..8463575d2 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/CHANGELOG.md @@ -0,0 +1,27 @@ +# Release History: opentelemetry-sdk + +All notable changes to the sdk project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Guideline to update the version + +Increment the: + +* MAJOR version when you make incompatible API/ABI changes, +* MINOR version when you add functionality in a backwards compatible manner, and +* PATCH version when you make backwards compatible bug fixes. + +## [Unreleased] + +## [0.1.0] 2020-12-17 + +### Added + +* Trace SDK experimental +* OTLP Exporter + +### Changed + +### Removed diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/CMakeLists.txt new file mode 100644 index 000000000..ca5af6334 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/CMakeLists.txt @@ -0,0 +1,44 @@ +add_library(opentelemetry_sdk INTERFACE) +target_include_directories( + opentelemetry_sdk + INTERFACE "$" + "$") + +set_target_properties(opentelemetry_sdk PROPERTIES EXPORT_NAME sdk) + +install( + TARGETS opentelemetry_sdk + EXPORT "${PROJECT_NAME}-target" + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + +install( + DIRECTORY include/opentelemetry/ + DESTINATION include/opentelemetry + FILES_MATCHING + PATTERN "*config.h") + +set(LOGS_EXCLUDE_PATTERN "") +if(NOT WITH_LOGS_PREVIEW) + set(LOGS_EXCLUDE_PATTERN "logs") +endif() + +set(METRICS_EXCLUDE_PATTERN "") +if(NOT WITH_METRICS_PREVIEW) + set(METRICS_EXCLUDE_PATTERN "_metrics") +endif() + +install( + DIRECTORY include/opentelemetry/sdk + DESTINATION include/opentelemetry + FILES_MATCHING + PATTERN "*.h" + PATTERN "${METRICS_EXCLUDE_PATTERN}" EXCLUDE + PATTERN "${LOGS_EXCLUDE_PATTERN}" EXCLUDE) + +add_subdirectory(src) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/aggregator.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/aggregator.h new file mode 100644 index 000000000..f6821d731 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/aggregator.h @@ -0,0 +1,156 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_METRICS_PREVIEW + +# include +# include +# include "opentelemetry/_metrics/instrument.h" +# include "opentelemetry/common/timestamp.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +enum class AggregatorKind +{ + Counter = 0, + MinMaxSumCount = 1, + Gauge = 2, + Sketch = 3, + Histogram = 4, + Exact = 5, +}; + +/* + * Performs calculations necessary to combine updates from instruments into an + * insightful value. + * Also stores current instrument values and checkpoints collected at intervals + * governing the entire pipeline. + */ +template +class Aggregator +{ +public: + Aggregator() = default; + + virtual ~Aggregator() = default; + + /** + * Receives a captured value from the instrument and applies it to the current aggregator value. + * + * @param val, the raw value used in aggregation + * @return none + */ + virtual void update(T val) = 0; + + /** + * Checkpoints the current value. This function will overwrite the current checkpoint with the + * current value. + * + * @param none + * @return none + */ + virtual void checkpoint() = 0; + + /** + * Merges the values of two aggregators in a semantically accurate manner. + * Merging will occur differently for different aggregators depending on the + * way values are tracked. + * + * @param other, the aggregator with merge with + * @return none + */ + void merge(Aggregator *other); + + /** + * Returns the checkpointed value + * + * @param none + * @return the value of the checkpoint + */ + virtual std::vector get_checkpoint() = 0; + + /** + * Returns the current value + * + * @param none + * @return the present aggregator value + */ + virtual std::vector get_values() = 0; + + /** + * Returns the instrument kind which this aggregator is associated with + * + * @param none + * @return the InstrumentKind of the aggregator's owner + */ + virtual opentelemetry::metrics::InstrumentKind get_instrument_kind() final { return kind_; } + + /** + * Returns the type of this aggregator + * + * @param none + * @return the AggregatorKind of this instrument + */ + virtual AggregatorKind get_aggregator_kind() final { return agg_kind_; } + + /** + * Getter function for updated_ protected var + * + * @return A bool indicating wether or not this aggregator has been updated + * in the most recent collection interval. + */ + virtual bool is_updated() final { return updated_; } + + // virtual function to be overridden for the Histogram Aggregator + virtual std::vector get_boundaries() { return std::vector(); } + + // virtual function to be overridden for the Histogram Aggregator + virtual std::vector get_counts() { return std::vector(); } + + // virtual function to be overridden for Exact and Sketch Aggregators + virtual bool get_quant_estimation() { return false; } + + // virtual function to be overridden for Exact and Sketch Aggregators + virtual T get_quantiles(double q) { return values_[0]; } + + // virtual function to be overridden for Sketch Aggregator + virtual double get_error_bound() { return 0; } + + // virtual function to be overridden for Sketch Aggregator + virtual size_t get_max_buckets() { return 0; } + + // virtual function to be overridden for Gauge Aggregator + virtual opentelemetry::common::SystemTimestamp get_checkpoint_timestamp() + { + return opentelemetry::common::SystemTimestamp(); + } + + // Custom copy constructor to handle the mutex + Aggregator(const Aggregator &cp) + { + values_ = cp.values_; + checkpoint_ = cp.checkpoint_; + kind_ = cp.kind_; + agg_kind_ = cp.agg_kind_; + // use default initialized mutex as they cannot be copied + } + +protected: + std::vector values_; + std::vector checkpoint_; + opentelemetry::metrics::InstrumentKind kind_; + std::mutex mu_; + AggregatorKind agg_kind_; + bool updated_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/counter_aggregator.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/counter_aggregator.h new file mode 100644 index 000000000..b9842e8e0 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/counter_aggregator.h @@ -0,0 +1,108 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_METRICS_PREVIEW + +# include +# include +# include "opentelemetry/_metrics/instrument.h" +# include "opentelemetry/sdk/_metrics/aggregator/aggregator.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +template +class CounterAggregator final : public Aggregator +{ + +public: + CounterAggregator(opentelemetry::metrics::InstrumentKind kind) + { + this->kind_ = kind; + this->values_ = std::vector(1, 0); + this->checkpoint_ = std::vector(1, 0); + this->agg_kind_ = AggregatorKind::Counter; + } + + /** + * Receives a captured value from the instrument and applies it to the current aggregator value. + * + * @param val, the raw value used in aggregation + * @return none + */ + void update(T val) override + { + this->mu_.lock(); + this->updated_ = true; + this->values_[0] += val; // atomic operation + this->mu_.unlock(); + } + + /** + * Checkpoints the current value. This function will overwrite the current checkpoint with the + * current value. + * + * @param none + * @return none + */ + void checkpoint() override + { + this->mu_.lock(); + this->updated_ = false; + this->checkpoint_ = this->values_; + this->values_[0] = 0; + this->mu_.unlock(); + } + + /** + * Merges the values of two aggregators in a semantically accurate manner. + * In this case, merging only requires the the current values of the two aggregators be summed. + * + * @param other, the aggregator with merge with + * @return none + */ + void merge(CounterAggregator other) + { + if (this->agg_kind_ == other.agg_kind_) + { + this->mu_.lock(); + this->values_[0] += other.values_[0]; + this->checkpoint_[0] += other.checkpoint_[0]; + this->mu_.unlock(); + } + else + { +# if __EXCEPTIONS + throw std::invalid_argument("Aggregators of different types cannot be merged."); +# else + std::terminate(); +# endif + } + } + + /** + * Returns the checkpointed value + * + * @param none + * @return the value of the checkpoint + */ + virtual std::vector get_checkpoint() override { return this->checkpoint_; } + + /** + * Returns the current values + * + * @param none + * @return the present aggregator values + */ + virtual std::vector get_values() override { return this->values_; } +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/exact_aggregator.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/exact_aggregator.h new file mode 100644 index 000000000..44ea717a0 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/exact_aggregator.h @@ -0,0 +1,169 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_METRICS_PREVIEW + +# include "opentelemetry/_metrics/instrument.h" +# include "opentelemetry/sdk/_metrics/aggregator/aggregator.h" +# include "opentelemetry/version.h" + +# include +# include +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +/** + * This aggregator has two modes. In-order and quantile estimation. + * + * The first mode simply stores all values sent to the Update() + * function in a vector and maintains the order they were sent in. + * + * The second mode also stores all values sent to the Update() + * function in a vector but sorts this vector when Checkpoint() + * is called. This mode also includes a function, Quantile(), + * that estimates the quantiles of the recorded data. + * + * @tparam T the type of values stored in this aggregator. + */ +template +class ExactAggregator : public Aggregator +{ +public: + ExactAggregator(opentelemetry::metrics::InstrumentKind kind, bool quant_estimation = false) + { + static_assert(std::is_arithmetic::value, "Not an arithmetic type"); + this->kind_ = kind; + this->checkpoint_ = this->values_; + this->agg_kind_ = AggregatorKind::Exact; + quant_estimation_ = quant_estimation; + } + + ~ExactAggregator() = default; + + ExactAggregator(const ExactAggregator &cp) + { + this->values_ = cp.values_; + this->checkpoint_ = cp.checkpoint_; + this->kind_ = cp.kind_; + this->agg_kind_ = cp.agg_kind_; + quant_estimation_ = cp.quant_estimation_; + // use default initialized mutex as they cannot be copied + } + + /** + * Receives a captured value from the instrument and adds it to the values_ vector. + * + * @param val, the raw value used in aggregation + */ + void update(T val) override + { + this->mu_.lock(); + this->updated_ = true; + this->values_.push_back(val); + this->mu_.unlock(); + } + + /** + * Checkpoints the current values. This function will overwrite the current checkpoint with the + * current value. Sorts the values_ vector if quant_estimation_ == true + * + */ + void checkpoint() override + { + this->mu_.lock(); + this->updated_ = false; + if (quant_estimation_) + { + std::sort(this->values_.begin(), this->values_.end()); + } + this->checkpoint_ = this->values_; + this->values_.clear(); + this->mu_.unlock(); + } + + /** + * Merges two exact aggregators' values_ vectors together. + * + * @param other the aggregator to merge with this aggregator + */ + void merge(const ExactAggregator &other) + { + if (this->kind_ == other.kind_) + { + this->mu_.lock(); + // First merge values + this->values_.insert(this->values_.end(), other.values_.begin(), other.values_.end()); + // Now merge checkpoints + this->checkpoint_.insert(this->checkpoint_.end(), other.checkpoint_.begin(), + other.checkpoint_.end()); + this->mu_.unlock(); + } + else + { + // Log error + return; + } + } + + /** + * Performs quantile estimation on the checkpoint vector in this aggregator. + * This function only works if quant_estimation_ == true. + * @param q the quantile to estimate. 0 <= q <= 1 + * @return the nearest value in the vector to the exact quantile. + */ + T get_quantiles(double q) override + { + if (!quant_estimation_) + { +// Log error +# if __EXCEPTIONS + throw std::domain_error("Exact aggregator is not in quantile estimation mode!"); +# else + std::terminate(); +# endif + } + if (this->checkpoint_.size() == 0 || q < 0 || q > 1) + { +// Log error +# if __EXCEPTIONS + throw std::invalid_argument("Arg 'q' must be between 0 and 1, inclusive"); +# else + std::terminate(); +# endif + } + else if (q == 0 || this->checkpoint_.size() == 1) + { + return this->checkpoint_[0]; + } + else if (q == 1) + { + return this->checkpoint_[this->checkpoint_.size() - 1]; + } + else + { + float position = float(float(this->checkpoint_.size() - 1) * q); + int ceiling = int(ceil(position)); + return this->checkpoint_[ceiling]; + } + } + + //////////////////////////ACCESSOR FUNCTIONS////////////////////////// + std::vector get_checkpoint() override { return this->checkpoint_; } + + std::vector get_values() override { return this->values_; } + + bool get_quant_estimation() override { return quant_estimation_; } + +private: + bool quant_estimation_; // Used to switch between in-order and quantile estimation modes +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/gauge_aggregator.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/gauge_aggregator.h new file mode 100644 index 000000000..96119386d --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/gauge_aggregator.h @@ -0,0 +1,146 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_METRICS_PREVIEW + +# include "opentelemetry/_metrics/instrument.h" +# include "opentelemetry/common/timestamp.h" +# include "opentelemetry/sdk/_metrics/aggregator/aggregator.h" +# include "opentelemetry/version.h" + +# include +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +/** + * This aggregator stores and maintains a vector of + * type T where the contents of the vector simply + * include the last value recorded to the aggregator. + * The aggregator also maintains a timestamp of when + * the last value was recorded. + * + * @tparam T the type of values stored in this aggregator. + */ +template +class GaugeAggregator : public Aggregator +{ +public: + explicit GaugeAggregator(opentelemetry::metrics::InstrumentKind kind) + { + static_assert(std::is_arithmetic::value, "Not an arithmetic type"); + this->kind_ = kind; + this->values_ = std::vector(1, 0); + this->checkpoint_ = this->values_; + this->agg_kind_ = AggregatorKind::Gauge; + current_timestamp_ = opentelemetry::common::SystemTimestamp(std::chrono::system_clock::now()); + } + + ~GaugeAggregator() = default; + + GaugeAggregator(const GaugeAggregator &cp) + { + this->values_ = cp.values_; + this->checkpoint_ = cp.checkpoint_; + this->kind_ = cp.kind_; + this->agg_kind_ = cp.agg_kind_; + current_timestamp_ = cp.current_timestamp_; + // use default initialized mutex as they cannot be copied + } + + /** + * Receives a captured value from the instrument and applies it to the current aggregator value. + * + * @param val, the raw value used in aggregation + */ + void update(T val) override + { + this->mu_.lock(); + this->updated_ = true; + this->values_[0] = val; + current_timestamp_ = opentelemetry::common::SystemTimestamp(std::chrono::system_clock::now()); + this->mu_.unlock(); + } + + /** + * Checkpoints the current value. This function will overwrite the current checkpoint with the + * current value. + * + * @return none + */ + + void checkpoint() override + { + this->mu_.lock(); + + this->updated_ = false; + this->checkpoint_ = this->values_; + + // Reset the values to default + this->values_[0] = 0; + checkpoint_timestamp_ = current_timestamp_; + current_timestamp_ = opentelemetry::common::SystemTimestamp(std::chrono::system_clock::now()); + + this->mu_.unlock(); + } + + /** + * Merges two Gauge aggregators together + * + * @param other the aggregator to merge with this aggregator + */ + void merge(GaugeAggregator other) + { + if (this->kind_ == other.kind_) + { + this->mu_.lock(); + // First merge values + this->values_[0] = other.values_[0]; + // Now merge checkpoints + this->checkpoint_[0] = other.checkpoint_[0]; + current_timestamp_ = opentelemetry::common::SystemTimestamp(std::chrono::system_clock::now()); + this->mu_.unlock(); + } + else + { + // Log error + return; + } + } + + /** + * @return the value of the latest checkpoint + */ + std::vector get_checkpoint() override { return this->checkpoint_; } + + /** + * @return the latest checkpointed timestamp + */ + opentelemetry::common::SystemTimestamp get_checkpoint_timestamp() override + { + return checkpoint_timestamp_; + } + + /** + * @return the values_ vector stored in this aggregator + */ + std::vector get_values() override { return this->values_; } + + /** + * @return the timestamp of when the last value recorded + */ + opentelemetry::common::SystemTimestamp get_timestamp() { return current_timestamp_; } + +private: + opentelemetry::common::SystemTimestamp current_timestamp_; + opentelemetry::common::SystemTimestamp checkpoint_timestamp_; +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/histogram_aggregator.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/histogram_aggregator.h new file mode 100644 index 000000000..13e1e1244 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/histogram_aggregator.h @@ -0,0 +1,207 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_METRICS_PREVIEW + +# include +# include +# include +# include +# include "opentelemetry/_metrics/instrument.h" +# include "opentelemetry/sdk/_metrics/aggregator/aggregator.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +template +class HistogramAggregator final : public Aggregator +{ + +public: + /** + * Constructor for the histogram aggregator. A sorted vector of boundaries is expected and + * boundaries are doubles regardless of the aggregator's templated data type. + * + * Sum is stored in values_[0] + * Count is stored in position_[1] + */ + HistogramAggregator(opentelemetry::metrics::InstrumentKind kind, std::vector boundaries) + { + if (!std::is_sorted(boundaries.begin(), boundaries.end())) + { +# if __EXCEPTIONS + throw std::invalid_argument("Histogram boundaries must be monotonic."); +# else + std::terminate(); +# endif + } + this->kind_ = kind; + this->agg_kind_ = AggregatorKind::Histogram; + boundaries_ = boundaries; + this->values_ = std::vector(2, 0); + this->checkpoint_ = std::vector(2, 0); + bucketCounts_ = std::vector(boundaries_.size() + 1, 0); + bucketCounts_ckpt_ = std::vector(boundaries_.size() + 1, 0); + } + + /** + * Receives a captured value from the instrument and inserts it into the current histogram counts. + * + * Depending on the use case, a linear search or binary search based implementation may be + * preferred. In uniformly distributed datasets, linear search outperforms binary search until 512 + * buckets. However, if the distribution is strongly skewed right (for example server latency + * where most values may be <10ms but the range is from 0 - 1000 ms), a linear search could be + * superior even with more than 500 buckets as almost all values inserted would be at the + * beginning of the boundaries array and thus found more quickly through linear search. + * + * @param val, the raw value used in aggregation + * @return none + */ + void update(T val) override + { + this->mu_.lock(); + this->updated_ = true; + size_t bucketID = boundaries_.size(); + for (size_t i = 0; i < boundaries_.size(); i++) + { + if (val < boundaries_[i]) // concurrent read is thread-safe + { + bucketID = i; + break; + } + } + + // Alternate implementation with binary search + // auto pos = std::lower_bound (boundaries_.begin(), boundaries_.end(), val); + // bucketCounts_[pos-boundaries_.begin()] += 1; + + this->values_[0] += val; + this->values_[1] += 1; + bucketCounts_[bucketID] += 1; + this->mu_.unlock(); + } + + /** + * Checkpoints the current value. This function will overwrite the current checkpoint with the + * current value. + * + * @param none + * @return none + */ + void checkpoint() override + { + this->mu_.lock(); + this->updated_ = false; + this->checkpoint_ = this->values_; + this->values_[0] = 0; + this->values_[1] = 0; + bucketCounts_ckpt_ = bucketCounts_; + std::fill(bucketCounts_.begin(), bucketCounts_.end(), 0); + this->mu_.unlock(); + } + + /** + * Merges the values of two aggregators in a semantically accurate manner. + * A histogram aggregator can only be merged with another histogram aggregator that has the same + * boudnaries. A histogram merge first adds the sum and count values then iterates over the adds + * the bucket counts element by element. + * + * @param other, the aggregator with merge with + * @return none + */ + void merge(HistogramAggregator other) + { + this->mu_.lock(); + + // Ensure that incorrect types are not merged + if (this->agg_kind_ != other.agg_kind_) + { +# if __EXCEPTIONS + throw std::invalid_argument("Aggregators of different types cannot be merged."); +# else + std::terminate(); +# endif + // Reject histogram merges with differing boundary vectors + } + else if (other.boundaries_ != this->boundaries_) + { +# if __EXCEPTIONS + throw std::invalid_argument("Histogram boundaries do not match."); +# else + std::terminate(); +# endif + } + + this->values_[0] += other.values_[0]; + this->values_[1] += other.values_[1]; + + this->checkpoint_[0] += other.checkpoint_[0]; + this->checkpoint_[1] += other.checkpoint_[1]; + + for (size_t i = 0; i < bucketCounts_.size(); i++) + { + bucketCounts_[i] += other.bucketCounts_[i]; + bucketCounts_ckpt_[i] += other.bucketCounts_ckpt_[i]; + } + this->mu_.unlock(); + } + + /** + * Returns the checkpointed value + * + * @param none + * @return the value of the checkpoint + */ + std::vector get_checkpoint() override { return this->checkpoint_; } + + /** + * Returns the current values + * + * @param none + * @return the present aggregator values + */ + std::vector get_values() override { return this->values_; } + + /** + * Returns the bucket boundaries specified at this aggregator's creation. + * + * @param none + * @return the aggregator boundaries + */ + virtual std::vector get_boundaries() override { return boundaries_; } + + /** + * Returns the current counts for each bucket . + * + * @param none + * @return the aggregator bucket counts + */ + virtual std::vector get_counts() override { return bucketCounts_ckpt_; } + + HistogramAggregator(const HistogramAggregator &cp) + { + this->values_ = cp.values_; + this->checkpoint_ = cp.checkpoint_; + this->kind_ = cp.kind_; + this->agg_kind_ = cp.agg_kind_; + boundaries_ = cp.boundaries_; + bucketCounts_ = cp.bucketCounts_; + bucketCounts_ckpt_ = cp.bucketCounts_ckpt_; + // use default initialized mutex as they cannot be copied + } + +private: + std::vector boundaries_; + std::vector bucketCounts_; + std::vector bucketCounts_ckpt_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/min_max_sum_count_aggregator.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/min_max_sum_count_aggregator.h new file mode 100644 index 000000000..60628b567 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/min_max_sum_count_aggregator.h @@ -0,0 +1,159 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_METRICS_PREVIEW + +# include "opentelemetry/_metrics/instrument.h" +# include "opentelemetry/sdk/_metrics/aggregator/aggregator.h" +# include "opentelemetry/version.h" + +# include +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +const int MinValueIndex = 0; +const int MaxValueIndex = 1; +const int SumValueIndex = 2; +const int CountValueIndex = 3; +/** + * This aggregator stores and maintains a vector of + * type T where the contents in the vector are made + * up of the minimum value recorded to this instrument, + * the maximum value, the sum of all values, and the + * count of all values. + * + * @tparam T the type of values stored in this aggregator. + */ +template +class MinMaxSumCountAggregator : public Aggregator +{ +public: + explicit MinMaxSumCountAggregator(opentelemetry::metrics::InstrumentKind kind) + { + static_assert(std::is_arithmetic::value, "Not an arithmetic type"); + this->kind_ = kind; + this->values_ = std::vector(4, 0); // {min, max, sum, count} + this->checkpoint_ = this->values_; + this->agg_kind_ = AggregatorKind::MinMaxSumCount; + } + + ~MinMaxSumCountAggregator() = default; + + MinMaxSumCountAggregator(const MinMaxSumCountAggregator &cp) + { + this->values_ = cp.values_; + this->checkpoint_ = cp.checkpoint_; + this->kind_ = cp.kind_; + this->agg_kind_ = cp.agg_kind_; + // use default initialized mutex as they cannot be copied + } + + /** + * Receives a captured value from the instrument and applies it to the current aggregator value. + * + * @param val, the raw value used in aggregation + */ + void update(T val) override + { + this->mu_.lock(); + this->updated_ = true; + + if (this->values_[CountValueIndex] == 0 || val < this->values_[MinValueIndex]) // set min + this->values_[MinValueIndex] = val; + if (this->values_[CountValueIndex] == 0 || val > this->values_[MaxValueIndex]) // set max + this->values_[MaxValueIndex] = val; + + this->values_[SumValueIndex] += val; // compute sum + this->values_[CountValueIndex]++; // increment count + + this->mu_.unlock(); + } + + /** + * Checkpoints the current value. This function will overwrite the current checkpoint with the + * current value. + * + */ + void checkpoint() override + { + this->mu_.lock(); + this->updated_ = false; + this->checkpoint_ = this->values_; + // Reset the values + this->values_[MinValueIndex] = 0; + this->values_[MaxValueIndex] = 0; + this->values_[SumValueIndex] = 0; + this->values_[CountValueIndex] = 0; + this->mu_.unlock(); + } + + /** + * Merges two MinMaxSumCount aggregators together + * + * @param other the aggregator to merge with this aggregator + */ + void merge(const MinMaxSumCountAggregator &other) + { + if (this->kind_ == other.kind_) + { + this->mu_.lock(); + // First merge values + // set min + if (this->values_[CountValueIndex] == 0 || + other.values_[MinValueIndex] < this->values_[MinValueIndex]) + this->values_[MinValueIndex] = other.values_[MinValueIndex]; + // set max + if (this->values_[CountValueIndex] == 0 || + other.values_[MaxValueIndex] > this->values_[MaxValueIndex]) + this->values_[MaxValueIndex] = other.values_[MaxValueIndex]; + // set sum + this->values_[SumValueIndex] += other.values_[SumValueIndex]; + // set count + this->values_[CountValueIndex] += other.values_[CountValueIndex]; + + // Now merge checkpoints + if (this->checkpoint_[CountValueIndex] == 0 || + other.checkpoint_[MinValueIndex] < this->checkpoint_[MinValueIndex]) + this->checkpoint_[MinValueIndex] = other.checkpoint_[MinValueIndex]; + // set max + if (this->checkpoint_[CountValueIndex] == 0 || + other.checkpoint_[MaxValueIndex] > this->checkpoint_[MaxValueIndex]) + this->checkpoint_[MaxValueIndex] = other.checkpoint_[MaxValueIndex]; + // set sum + this->checkpoint_[SumValueIndex] += other.checkpoint_[SumValueIndex]; + // set count + this->checkpoint_[CountValueIndex] += other.checkpoint_[CountValueIndex]; + + this->mu_.unlock(); + } + else + { + // Log error + return; + } + } + + /** + * Returns the checkpointed value + * + * @return the value of the checkpoint + */ + std::vector get_checkpoint() override { return this->checkpoint_; } + + /** + * Returns the values currently held by the aggregator + * + * @return the values held by the aggregator + */ + std::vector get_values() override { return this->values_; } +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/sketch_aggregator.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/sketch_aggregator.h new file mode 100644 index 000000000..3942c9550 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/aggregator/sketch_aggregator.h @@ -0,0 +1,282 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_METRICS_PREVIEW + +# include +# include +# include +# include +# include +# include +# include +# include "opentelemetry/_metrics/instrument.h" +# include "opentelemetry/sdk/_metrics/aggregator/aggregator.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +/** Sketch Aggregators implement the DDSketch data type. Note that data is compressed + * by the DDSketch algorithm and users should be informed about its behavior before + * selecting it as the aggregation type. NOTE: The current implementation can only support + * non-negative values. + * + * Detailed information about the algorithm can be found in the following paper + * published by Datadog: http://www.vldb.org/pvldb/vol12/p2195-masson.pdf + */ + +template +class SketchAggregator final : public Aggregator +{ + +public: + /** + * Given the distribution of data this aggregator is designed for and its usage, the raw updates + *are stored in a map rather than a vector. + * + *@param kind, the instrument kind creating this aggregator + *@param error_bound, what is referred to as "alpha" in the DDSketch algorithm + *@param max_buckets, the maximum number of indices in the raw value map + */ + SketchAggregator(opentelemetry::metrics::InstrumentKind kind, + double error_bound, + size_t max_buckets = 2048) + { + + this->kind_ = kind; + this->agg_kind_ = AggregatorKind::Sketch; + this->values_ = std::vector(2, 0); // Sum in [0], Count in [1] + this->checkpoint_ = std::vector(2, 0); + max_buckets_ = max_buckets; + error_bound_ = error_bound; + gamma = (1 + error_bound) / (1 - error_bound); + } + + /** + * Update the aggregator with the new value. For a DDSketch aggregator, if the addition of this + * value creates a new bucket which is in excess of the maximum allowed size, the lowest indexes + * buckets are merged. + * + * @param val, the raw value used in aggregation + * @return none + */ + void update(T val) override + { + this->mu_.lock(); + this->updated_ = true; + int idx; + if (val == 0) + { + idx = (std::numeric_limits::min()); + } + else + { + idx = static_cast(ceil(log(val) / log(gamma))); + } + if (raw_.find(idx) != raw_.end()) + { + raw_[idx] += 1; + } + else + { + raw_[idx] = 1; + } + this->values_[1] += 1; + this->values_[0] += val; + if (raw_.size() > max_buckets_) + { + int minidx = raw_.begin()->first, minidxval = raw_.begin()->second; + raw_.erase(minidx); + raw_[raw_.begin()->first] += minidxval; + } + this->mu_.unlock(); + } + + /** + * Calculate and return the value of a user specified quantile. + * + * @param q, the quantile to calculate (for example 0.5 is equivalent to the 50th percentile) + */ + virtual T get_quantiles(double q) override + { + if (q < 0 || q > 1) + { +# if __EXCEPTIONS + throw std::invalid_argument("Quantile values must fall between 0 and 1"); +# else + std::terminate(); +# endif + } + auto iter = checkpoint_raw_.begin(); + int idx = iter->first; + int count = iter->second; + + while (count < (q * (this->checkpoint_[1] - 1)) && iter != checkpoint_raw_.end()) + { + iter++; + idx = iter->first; + count += iter->second; + } + return static_cast(round(2 * pow(gamma, idx) / (gamma + 1))); + } + + /** + * Checkpoints the current value. This function will overwrite the current checkpoint with the + * current value. + * + * @param none + * @return none + */ + void checkpoint() override + { + this->mu_.lock(); + this->updated_ = false; + this->checkpoint_ = this->values_; + checkpoint_raw_ = raw_; + this->values_[0] = 0; + this->values_[1] = 0; + raw_.clear(); + this->mu_.unlock(); + } + + /** + * Merges this sketch aggregator with another. The same bucket compression used when + * updating values is employed here to manage bucket size if the merging of aggregators + * results in more buckets than allowed. + * + * @param other, the aggregator with merge with + * @return none + */ + void merge(SketchAggregator other) + { + if (gamma != other.gamma) + { +# if __EXCEPTIONS + throw std::invalid_argument("Aggregators must have identical error tolerance"); +# else + std::terminate(); +# endif + } + else if (max_buckets_ != other.max_buckets_) + { +# if __EXCEPTIONS + throw std::invalid_argument("Aggregators must have the same maximum bucket allowance"); +# else + std::terminate(); +# endif + } + + this->mu_.lock(); + this->values_[0] += other.values_[0]; + this->values_[1] += other.values_[1]; + this->checkpoint_[0] += other.checkpoint_[0]; + this->checkpoint_[1] += other.checkpoint_[1]; + auto other_iter = other.raw_.begin(); + while (other_iter != other.raw_.end()) + { + raw_[other_iter->first] += other_iter->second; + if (raw_.size() > max_buckets_) + { + int minidx = raw_.begin()->first, minidxval = raw_.begin()->second; + raw_.erase(minidx); + raw_[raw_.begin()->first] += minidxval; + } + other_iter++; + } + auto other_ckpt_iter = other.checkpoint_raw_.begin(); + while (other_ckpt_iter != other.checkpoint_raw_.end()) + { + checkpoint_raw_[other_ckpt_iter->first] += other_ckpt_iter->second; + if (checkpoint_raw_.size() > max_buckets_) + { + int minidx = checkpoint_raw_.begin()->first, minidxval = checkpoint_raw_.begin()->second; + checkpoint_raw_.erase(minidx); + checkpoint_raw_[checkpoint_raw_.begin()->first] += minidxval; + } + other_ckpt_iter++; + } + this->mu_.unlock(); + } + + /** + * Returns the checkpointed value + * + * @param none + * @return the value of the checkpoint + */ + std::vector get_checkpoint() override { return this->checkpoint_; } + + /** + * Returns the current values + * + * @param none + * @return the present aggregator values + */ + std::vector get_values() override { return this->values_; } + + /** + * Returns the indices (or values) stored by this sketch aggregator. + * + * @param none + * @return a vector of all values the aggregator is currently tracking + */ + virtual std::vector get_boundaries() override + { + std::vector ret; + for (auto const &x : checkpoint_raw_) + { + ret.push_back(2 * pow(gamma, x.first) / (gamma + 1)); + } + return ret; + } + + /** + * Returns the error bound + * + * @param none + * @return the error bound specified during construction + */ + virtual double get_error_bound() override { return error_bound_; } + + /** + * Returns the maximum allowed buckets + * + * @param none + * @return the maximum allowed buckets + */ + virtual size_t get_max_buckets() override { return max_buckets_; } + + /** + * Returns the count of each value tracked by this sketch aggregator. These are returned + * in the same order as the indices returned by the get_boundaries function. + * + * @param none + * @return a vector of all counts for values tracked by the aggregator + */ + virtual std::vector get_counts() override + { + std::vector ret; + for (auto const &x : checkpoint_raw_) + { + ret.push_back(x.second); + } + return ret; + } + +private: + double gamma; + double error_bound_; + size_t max_buckets_; + std::map raw_; + std::map checkpoint_raw_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/async_instruments.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/async_instruments.h new file mode 100644 index 000000000..5213a4e2e --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/async_instruments.h @@ -0,0 +1,286 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_METRICS_PREVIEW + +# include +# include +# include +# include +# include +# include "opentelemetry/_metrics/async_instruments.h" +# include "opentelemetry/sdk/_metrics/aggregator/counter_aggregator.h" +# include "opentelemetry/sdk/_metrics/aggregator/min_max_sum_count_aggregator.h" +# include "opentelemetry/sdk/_metrics/instrument.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +# if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4250) // inheriting methods via dominance +# endif + +template +class ValueObserver : public AsynchronousInstrument, + virtual public opentelemetry::metrics::ValueObserver +{ + +public: + ValueObserver() = default; + + ValueObserver(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + bool enabled, + void (*callback)(opentelemetry::metrics::ObserverResult)) + : AsynchronousInstrument(name, + description, + unit, + enabled, + callback, + opentelemetry::metrics::InstrumentKind::ValueObserver) + {} + + /* + * Updates the instruments aggregator with the new value. The labels should + * contain the keys and values to be associated with this value. + * + * @param value is the numerical representation of the metric being captured + * @param labels the set of labels, as key-value pairs + */ + virtual void observe(T value, const opentelemetry::common::KeyValueIterable &labels) override + { + this->mu_.lock(); + std::string labelset = KvToString(labels); + if (boundAggregators_.find(labelset) == boundAggregators_.end()) + { + auto sp1 = std::shared_ptr>(new MinMaxSumCountAggregator(this->kind_)); + boundAggregators_.insert(std::make_pair(labelset, sp1)); + sp1->update(value); + } + else + { + boundAggregators_[labelset]->update(value); + } + this->mu_.unlock(); + } + + /* + * Activate the instrument's callback function to record a measurement. This + * function will be called by the specified controller at a regular interval. + * + * @param none + * @return none + */ + virtual void run() override + { + opentelemetry::metrics::ObserverResult res(this); + this->callback_(res); + } + + virtual std::vector GetRecords() override + { + this->mu_.lock(); + std::vector ret; + for (auto x : boundAggregators_) + { + x.second->checkpoint(); + ret.push_back(Record(this->GetName(), this->GetDescription(), x.first, x.second)); + } + boundAggregators_.clear(); + this->mu_.unlock(); + return ret; + } + + // Public mapping from labels (stored as strings) to their respective aggregators + std::unordered_map>> boundAggregators_; +}; + +template +class SumObserver : public AsynchronousInstrument, + virtual public opentelemetry::metrics::SumObserver +{ + +public: + SumObserver() = default; + + SumObserver(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + bool enabled, + void (*callback)(opentelemetry::metrics::ObserverResult)) + : AsynchronousInstrument(name, + description, + unit, + enabled, + callback, + opentelemetry::metrics::InstrumentKind::SumObserver) + {} + + /* + * Updates the instruments aggregator with the new value. The labels should + * contain the keys and values to be associated with this value. + * + * @param value is the numerical representation of the metric being captured + * @param labels the set of labels, as key-value pairs + */ + virtual void observe(T value, const opentelemetry::common::KeyValueIterable &labels) override + { + this->mu_.lock(); + std::string labelset = KvToString(labels); + if (boundAggregators_.find(labelset) == boundAggregators_.end()) + { + auto sp1 = std::shared_ptr>(new CounterAggregator(this->kind_)); + boundAggregators_.insert(std::make_pair(labelset, sp1)); + if (value < 0) + { +# if __EXCEPTIONS + throw std::invalid_argument("Counter instrument updates must be non-negative."); +# else + std::terminate(); +# endif + } + else + { + sp1->update(value); + } + } + else + { + if (value < 0) + { +# if __EXCEPTIONS + throw std::invalid_argument("Counter instrument updates must be non-negative."); +# else + std::terminate(); +# endif + } + else + { + boundAggregators_[labelset]->update(value); + } + } + this->mu_.unlock(); + } + + /* + * Activate the intsrument's callback function to record a measurement. This + * function will be called by the specified controller at a regular interval. + * + * @param none + * @return none + */ + virtual void run() override + { + opentelemetry::metrics::ObserverResult res(this); + this->callback_(res); + } + + virtual std::vector GetRecords() override + { + this->mu_.lock(); + std::vector ret; + for (auto x : boundAggregators_) + { + x.second->checkpoint(); + ret.push_back(Record(this->GetName(), this->GetDescription(), x.first, x.second)); + } + boundAggregators_.clear(); + this->mu_.unlock(); + return ret; + } + + // Public mapping from labels (stored as strings) to their respective aggregators + std::unordered_map>> boundAggregators_; +}; + +template +class UpDownSumObserver : public AsynchronousInstrument, + virtual public opentelemetry::metrics::UpDownSumObserver +{ + +public: + UpDownSumObserver() = default; + + UpDownSumObserver(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + bool enabled, + void (*callback)(opentelemetry::metrics::ObserverResult)) + : AsynchronousInstrument(name, + description, + unit, + enabled, + callback, + opentelemetry::metrics::InstrumentKind::UpDownSumObserver) + {} + + /* + * Updates the instruments aggregator with the new value. The labels should + * contain the keys and values to be associated with this value. + * + * @param value is the numerical representation of the metric being captured + * @param labels the set of labels, as key-value pairs + */ + virtual void observe(T value, const opentelemetry::common::KeyValueIterable &labels) override + { + this->mu_.lock(); + std::string labelset = KvToString(labels); + if (boundAggregators_.find(labelset) == boundAggregators_.end()) + { + auto sp1 = std::shared_ptr>(new CounterAggregator(this->kind_)); + boundAggregators_.insert(std::make_pair(labelset, sp1)); + sp1->update(value); + } + else + { + boundAggregators_[labelset]->update(value); + } + this->mu_.unlock(); + } + + /* + * Activate the intsrument's callback function to record a measurement. This + * function will be called by the specified controller at a regular interval. + * + * @param none + * @return none + */ + virtual void run() override + { + opentelemetry::metrics::ObserverResult res(this); + this->callback_(res); + } + + virtual std::vector GetRecords() override + { + this->mu_.lock(); + std::vector ret; + for (auto x : boundAggregators_) + { + x.second->checkpoint(); + ret.push_back(Record(this->GetName(), this->GetDescription(), x.first, x.second)); + } + boundAggregators_.clear(); + this->mu_.unlock(); + return ret; + } + + // Public mapping from labels (stored as strings) to their respective aggregators + std::unordered_map>> boundAggregators_; +}; + +# if defined(_MSC_VER) +# pragma warning(pop) +# endif + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/controller.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/controller.h new file mode 100644 index 000000000..66a57a2ef --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/controller.h @@ -0,0 +1,154 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_METRICS_PREVIEW + +# include +# include +# include +# include +# include + +# include "opentelemetry/_metrics/instrument.h" +# include "opentelemetry/common/macros.h" +# include "opentelemetry/nostd/unique_ptr.h" +# include "opentelemetry/sdk/_metrics/exporter.h" +# include "opentelemetry/sdk/_metrics/meter.h" +# include "opentelemetry/sdk/_metrics/processor.h" +# include "opentelemetry/sdk/_metrics/record.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +class PushController +{ + +public: + PushController(nostd::shared_ptr meter, + nostd::unique_ptr exporter, + nostd::shared_ptr processor, + double period, + int timeout = 30) + { + meter_ = meter; + exporter_ = std::move(exporter); + processor_ = processor; + timeout_ = (unsigned int)(timeout * 1000000); // convert seconds to microseconds + period_ = (unsigned int)(period * 1000000); + } + + /* + * Used to check if the metrics pipeline is currently active + * + * @param none + * @return true when active, false when on standby. This is a best guess estimate + * and the boolean from start() should be used to determine wheher the pipeline + * was initiated successfully. + */ + bool isActive() { return active_.load(); } + + /* + * Begins the data processing and export pipeline. The function first ensures that the pipeline + * is not already running. If not, it begins and detaches a new thread for the Controller's run + * function which periodically polls the instruments for their data. + * + * @param none + * @return a boolean which is true when the pipeline is successfully started and false when + * already active + */ + bool start() + { + if (!active_.exchange(true)) + { + runner_ = std::thread(&PushController::run, this); + return true; + } + return false; + } + + /* + * Ends the processing and export pipeline then exports metrics one last time + * before returning. + * + * @param none + * @return none + */ + void stop() + { + if (active_.exchange(false)) + { + if (runner_.joinable()) + { + runner_.join(); + } + tick(); // flush metrics sitting in the processor + } + } + +private: + /* + * Run the tick function at a regular interval. This function + * should be run in its own thread. + * + * Used to wait between collection intervals. + */ + void run() + { + if (!running_.exchange(true)) + { + while (active_.load()) + { + tick(); + std::this_thread::sleep_for(std::chrono::microseconds(period_)); + } + running_.exchange(false); + } + } + + /* + * Tick + * + * Called at regular intervals, this function collects all values from the + * member variable meter_, then sends them to the processor_ for + * processing. After the records have been processed they are sent to the + * exporter_ to be exported. + * + */ + void tick() + { + this->mu_.lock(); +# ifdef OPENTELEMETRY_RTTI_ENABLED + std::vector collected = dynamic_cast(meter_.get())->Collect(); +# else + std::vector collected = static_cast(meter_.get())->Collect(); +# endif + for (const auto &rec : collected) + { + processor_->process(rec); + } + collected = processor_->CheckpointSelf(); + processor_->FinishedCollection(); + exporter_->Export(collected); + this->mu_.unlock(); + } + + nostd::shared_ptr meter_; + nostd::unique_ptr exporter_; + nostd::shared_ptr processor_; + std::thread runner_; + std::mutex mu_; + std::atomic active_ = ATOMIC_VAR_INIT(false); + std::atomic running_ = ATOMIC_VAR_INIT(false); + unsigned int period_; + unsigned int timeout_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/exporter.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/exporter.h new file mode 100644 index 000000000..aae416527 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/exporter.h @@ -0,0 +1,35 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_METRICS_PREVIEW + +# include +# include "opentelemetry/sdk/_metrics/record.h" +# include "opentelemetry/sdk/common/exporter_utils.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +/** + * MetricsExporter defines the interface that protocol-specific span exporters must + * implement. + */ +class MetricsExporter +{ +public: + virtual ~MetricsExporter() = default; + + /** + * Exports a vector of Records. This method must not be called + * concurrently for the same exporter instance. + * @param records a vector of unique pointers to metric records + */ + virtual sdk::common::ExportResult Export(const std::vector &records) noexcept = 0; +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/instrument.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/instrument.h new file mode 100644 index 000000000..51111be5e --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/instrument.h @@ -0,0 +1,312 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_METRICS_PREVIEW + +# include +# include +# include +# include +# include +# include +# include +# include "opentelemetry/_metrics/instrument.h" +# include "opentelemetry/sdk/_metrics/aggregator/aggregator.h" +# include "opentelemetry/sdk/_metrics/record.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +# if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4250) // inheriting methods via dominance +# endif + +class Instrument : virtual public opentelemetry::metrics::Instrument +{ + +public: + Instrument() = default; + + Instrument(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + bool enabled, + opentelemetry::metrics::InstrumentKind kind) + : name_(name), description_(description), unit_(unit), enabled_(enabled), kind_(kind) + {} + + // Returns true if the instrument is enabled and collecting data + virtual bool IsEnabled() override { return enabled_; } + + // Return the instrument name + virtual nostd::string_view GetName() override { return name_; } + + // Return the instrument description + virtual nostd::string_view GetDescription() override { return description_; } + + // Return the insrument's units of measurement + virtual nostd::string_view GetUnits() override { return unit_; } + + virtual opentelemetry::metrics::InstrumentKind GetKind() override { return this->kind_; } + +protected: + std::string name_; + std::string description_; + std::string unit_; + bool enabled_; + std::mutex mu_; + opentelemetry::metrics::InstrumentKind kind_; +}; + +template +class BoundSynchronousInstrument + : public Instrument, + virtual public opentelemetry::metrics::BoundSynchronousInstrument +{ + +public: + BoundSynchronousInstrument() = default; + + BoundSynchronousInstrument(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + bool enabled, + opentelemetry::metrics::InstrumentKind kind, + std::shared_ptr> agg) + : Instrument(name, description, unit, enabled, kind), agg_(agg) + { + this->inc_ref(); // increase reference count when instantiated + } + + /** + * Frees the resources associated with this Bound Instrument. + * The Metric from which this instrument was created is not impacted. + * + * @param none + * @return void + */ + virtual void unbind() override + { + this->mu_.lock(); + ref_ -= 1; + this->mu_.unlock(); + } + + /** + * Increments the reference count. This function is used when binding or instantiating. + * + * @param none + * @return void + */ + virtual void inc_ref() override + { + this->mu_.lock(); + ref_ += 1; + this->mu_.unlock(); + } + + /** + * Returns the current reference count of the instrument. This value is used to + * later in the pipeline remove stale instruments. + * + * @param none + * @return current ref count of the instrument + */ + virtual int get_ref() override + { + this->mu_.lock(); + auto ret = ref_; + this->mu_.unlock(); + return ret; + } + + /** + * Records a single synchronous metric event via a call to the aggregator. + * Since this is a bound synchronous instrument, labels are not required in + * metric capture calls. + * + * @param value is the numerical representation of the metric being captured + * @return void + */ + virtual void update(T value) override + { + this->mu_.lock(); + agg_->update(value); + this->mu_.unlock(); + } + + /** + * Returns the aggregator responsible for meaningfully combining update values. + * + * @param none + * @return the aggregator assigned to this instrument + */ + virtual std::shared_ptr> GetAggregator() final { return agg_; } + +private: + std::shared_ptr> agg_; + int ref_ = 0; +}; + +template +class SynchronousInstrument : public Instrument, + virtual public opentelemetry::metrics::SynchronousInstrument +{ + +public: + SynchronousInstrument() = default; + + SynchronousInstrument(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + bool enabled, + opentelemetry::metrics::InstrumentKind kind) + : Instrument(name, description, unit, enabled, kind) + {} + + /** + * Returns a Bound Instrument associated with the specified labels. Multiples requests + * with the same set of labels may return the same Bound Instrument instance. + * + * It is recommended that callers keep a reference to the Bound Instrument + * instead of repeatedly calling this operation. + * + * @param labels the set of labels, as key-value pairs + * @return a Bound Instrument + */ + virtual nostd::shared_ptr> bind( + const opentelemetry::common::KeyValueIterable &labels) override + { + return nostd::shared_ptr>(); + } + + // This function is necessary for batch recording and should NOT be called by the user + virtual void update(T value, const opentelemetry::common::KeyValueIterable &labels) override = 0; + + /** + * Checkpoints instruments and returns a set of records which are ready for processing. + * This method should ONLY be called by the Meter Class as part of the export pipeline + * as it also prunes bound instruments with no active references. + * + * @param none + * @return vector of Records which hold the data attached to this synchronous instrument + */ + virtual std::vector GetRecords() = 0; +}; + +template +class AsynchronousInstrument : public Instrument, + virtual public opentelemetry::metrics::AsynchronousInstrument +{ + +public: + AsynchronousInstrument() = default; + + AsynchronousInstrument(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + bool enabled, + void (*callback)(opentelemetry::metrics::ObserverResult), + opentelemetry::metrics::InstrumentKind kind) + : Instrument(name, description, unit, enabled, kind) + { + this->callback_ = callback; + } + + /** + * Captures data through a manual call rather than the automatic collection process instituted + * in the run function. Asynchronous instruments are generally expected to obtain data from + * their callbacks rather than direct calls. This function is used by the callback to store data. + * + * @param value is the numerical representation of the metric being captured + * @param labels is the numerical representation of the metric being captured + * @return none + */ + virtual void observe(T value, const opentelemetry::common::KeyValueIterable &labels) override = 0; + + virtual std::vector GetRecords() = 0; + + /** + * Captures data by activating the callback function associated with the + * instrument and storing its return value. Callbacks for asynchronous + * instruments are defined during construction. + * + * @param none + * @return none + */ + virtual void run() override = 0; +}; + +// Helper functions for turning a common::KeyValueIterable into a string +inline void print_value(std::stringstream &ss, + opentelemetry::common::AttributeValue &value, + bool jsonTypes = false) +{ + switch (value.index()) + { + case opentelemetry::common::AttributeType::kTypeString: + + ss << nostd::get(value); + + break; + default: +# if __EXCEPTIONS + throw std::invalid_argument("Labels must be strings"); +# else + std::terminate(); +# endif + break; + } +}; + +// Utility function which converts maps to strings for better performance +inline std::string mapToString(const std::map &conv) +{ + std::stringstream ss; + ss << "{"; + for (auto i : conv) + { + ss << i.first << ':' << i.second << ','; + } + ss << "}"; + return ss.str(); +} + +inline std::string KvToString(const opentelemetry::common::KeyValueIterable &kv) noexcept +{ + std::stringstream ss; + ss << "{"; + size_t size = kv.size(); + if (size) + { + size_t i = 1; + kv.ForEachKeyValue( + [&](nostd::string_view key, opentelemetry::common::AttributeValue value) noexcept { + ss << key << ":"; + print_value(ss, value, true); + if (size != i) + { + ss << ","; + } + i++; + return true; + }); + }; + ss << "}"; + return ss.str(); +} + +# if defined(_MSC_VER) +# pragma warning(pop) +# endif + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/meter.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/meter.h new file mode 100644 index 000000000..a91a3ed2b --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/meter.h @@ -0,0 +1,392 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_METRICS_PREVIEW + +# include "opentelemetry/_metrics/meter.h" +# include "opentelemetry/nostd/shared_ptr.h" +# include "opentelemetry/sdk/_metrics/async_instruments.h" +# include "opentelemetry/sdk/_metrics/instrument.h" +# include "opentelemetry/sdk/_metrics/record.h" +# include "opentelemetry/sdk/_metrics/sync_instruments.h" + +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +class Meter : public opentelemetry::metrics::Meter +{ +public: + explicit Meter(std::string library_name, std::string library_version = "") + { + library_name_ = library_name; + library_version_ = library_version; + } + + /** + * Creates a Counter with the passed characteristics and returns a shared_ptr to that Counter. + * + * @param name the name of the new Counter. + * @param description a brief description of what the Counter is used for. + * @param unit the unit of metric values following https://unitsofmeasure.org/ucum.html. + * @param enabled a boolean value that turns on or off the metric instrument. + * @return a shared pointer to the created Counter. + * @throws invalid_argument exception if name is null or does not conform to OTel syntax. + */ + nostd::shared_ptr> NewShortCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override; + + nostd::shared_ptr> NewIntCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override; + + nostd::shared_ptr> NewFloatCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override; + + nostd::shared_ptr> NewDoubleCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override; + + /** + * Creates an UpDownCounter with the passed characteristics and returns a shared_ptr to that + * UpDownCounter. + * + * @param name the name of the new UpDownCounter. + * @param description a brief description of what the UpDownCounter is used for. + * @param unit the unit of metric values following https://unitsofmeasure.org/ucum.html. + * @param enabled a boolean value that turns on or off the metric instrument. + * @return a shared pointer to the created UpDownCounter. + * @throws invalid_argument exception if name is null or does not conform to OTel syntax. + */ + nostd::shared_ptr> NewShortUpDownCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override; + + nostd::shared_ptr> NewIntUpDownCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override; + + nostd::shared_ptr> NewFloatUpDownCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override; + + nostd::shared_ptr> NewDoubleUpDownCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override; + + /** + * Creates a ValueRecorder with the passed characteristics and returns a shared_ptr to that + * ValueRecorder. + * + * @param name the name of the new ValueRecorder. + * @param description a brief description of what the ValueRecorder is used for. + * @param unit the unit of metric values following https://unitsofmeasure.org/ucum.html. + * @param enabled a boolean value that turns on or off the metric instrument. + * @return a shared pointer to the created DoubleValueRecorder. + * @throws invalid_argument exception if name is null or does not conform to OTel syntax. + */ + nostd::shared_ptr> NewShortValueRecorder( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override; + + nostd::shared_ptr> NewIntValueRecorder( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override; + + nostd::shared_ptr> NewFloatValueRecorder( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override; + + nostd::shared_ptr> NewDoubleValueRecorder( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override; + + /** + * Creates a SumObserver with the passed characteristics and returns a shared_ptr to that + * SumObserver. + * + * @param name the name of the new SumObserver. + * @param description a brief description of what the SumObserver is used for. + * @param unit the unit of metric values following https://unitsofmeasure.org/ucum.html. + * @param enabled a boolean value that turns on or off the metric instrument. + * @param callback the function to be observed by the instrument. + * @return a shared pointer to the created SumObserver. + * @throws invalid_argument exception if name is null or does not conform to OTel syntax. + */ + nostd::shared_ptr> NewShortSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(opentelemetry::metrics::ObserverResult)) override; + + nostd::shared_ptr> NewIntSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(opentelemetry::metrics::ObserverResult)) override; + + nostd::shared_ptr> NewFloatSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(opentelemetry::metrics::ObserverResult)) override; + + nostd::shared_ptr> NewDoubleSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(opentelemetry::metrics::ObserverResult)) override; + + /** + * Creates an UpDownSumObserver with the passed characteristics and returns a shared_ptr to + * that UpDowNSumObserver. + * + * @param name the name of the new UpDownSumObserver. + * @param description a brief description of what the UpDownSumObserver is used for. + * @param unit the unit of metric values following https://unitsofmeasure.org/ucum.html. + * @param enabled a boolean value that turns on or off the metric instrument. + * @param callback the function to be observed by the instrument. + * @return a shared pointer to the created UpDownSumObserver. + * @throws invalid_argument exception if name is null or does not conform to OTel syntax. + */ + nostd::shared_ptr> NewShortUpDownSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(opentelemetry::metrics::ObserverResult)) override; + + nostd::shared_ptr> NewIntUpDownSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(opentelemetry::metrics::ObserverResult)) override; + + nostd::shared_ptr> NewFloatUpDownSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(opentelemetry::metrics::ObserverResult)) override; + + nostd::shared_ptr> NewDoubleUpDownSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(opentelemetry::metrics::ObserverResult)) override; + + /** + * Creates a ValueObserver with the passed characteristics and returns a shared_ptr to that + * ValueObserver. + * + * @param name the name of the new ValueObserver. + * @param description a brief description of what the ValueObserver is used for. + * @param unit the unit of metric values following https://unitsofmeasure.org/ucum.html. + * @param enabled a boolean value that turns on or off the metric instrument. + * @param callback the function to be observed by the instrument. + * @return a shared pointer to the created ValueObserver. + * @throws invalid_argument exception if name is null or does not conform to OTel syntax. + */ + nostd::shared_ptr> NewShortValueObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(opentelemetry::metrics::ObserverResult)) override; + + nostd::shared_ptr> NewIntValueObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(opentelemetry::metrics::ObserverResult)) override; + + nostd::shared_ptr> NewFloatValueObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(opentelemetry::metrics::ObserverResult)) override; + + nostd::shared_ptr> NewDoubleValueObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(opentelemetry::metrics::ObserverResult)) override; + + /** + * Utility method that allows users to atomically record measurements to a set of + * synchronous metric instruments with a common set of labels. + * + * @param labels the set of labels to associate with this recorder. + * @param values a span of pairs where the first element of the pair is a metric instrument + * to record to, and the second element is the value to update that instrument with. + */ + void RecordShortBatch( + const opentelemetry::common::KeyValueIterable &labels, + nostd::span *> instruments, + nostd::span values) noexcept override; + + void RecordIntBatch(const opentelemetry::common::KeyValueIterable &labels, + nostd::span *> instruments, + nostd::span values) noexcept override; + + void RecordFloatBatch( + const opentelemetry::common::KeyValueIterable &labels, + nostd::span *> instruments, + nostd::span values) noexcept override; + + void RecordDoubleBatch( + const opentelemetry::common::KeyValueIterable &labels, + nostd::span *> instruments, + nostd::span values) noexcept override; + + /** + * An SDK-only function that checkpoints the aggregators of all instruments created from + * this meter, creates a {@code Record} out of them, and sends them for export. + * + * @return A vector of {@code Records} to be sent to the processor. + */ + std::vector Collect() noexcept; + +private: + /** + * A private function that creates records from all synchronous instruments created from + * this meter. + * + * @param records A reference to the vector to push the new records to. + */ + void CollectMetrics(std::vector &records); + + /** + * Helper function to collect Records from a single synchronous instrument + * + * @tparam T The integral type of the instrument to collect from. + * @param i A map iterator pointing to the instrument to collect from + * @param records The vector to add the new records to. + */ + template + void CollectSingleSyncInstrument( + typename std::map>>::iterator + i, + std::vector &records); + + /** + * A private function that creates records from all asynchronous instruments created from + * this meter. + * + * @param records A reference to the vector to push the new records to. + */ + void CollectObservers(std::vector &records); + + /** + * Helper function to collect Records from a single asynchronous instrument + * + * @tparam T The integral type of the instrument to collect from. + * @param i A map iterator pointing to the instrument to collect from + * @param records The vector to add the new records to. + */ + template + void CollectSingleAsyncInstrument( + typename std::map< + std::string, + std::shared_ptr>>::iterator i, + std::vector &records); + + /** + * Utility function used by the meter that checks if a user-passed name abides by OpenTelemetry + * naming rules. The rules are as follows: + * 1. The name must not be empty. + * 2. The name must not start with a digit, a space, or any punctuation. + * 3. The name must only have the following chaacters: + * All alphanumeric characters, '.', '_' and '-'. + * + * @param name The name to be examined for legality. + * @return A bool representing whether the name is valid by the OpenTelemetry syntax rules. + */ + bool IsValidName(nostd::string_view name); + + /** + * A utility function used by the meter to determine whether an instrument of a specified + * name already exists in this meter. + * + * @param name The name to examine. + * @return A boolean representing whether the name has already been used by this meter. + */ + bool NameAlreadyUsed(nostd::string_view name); + + /* + * All instruments must be stored in a map so the meter can collect on these instruments. + * Additionally, when creating a new instrument, the meter must check if an instrument of the same + * name already exists. + */ + std::map>> + short_metrics_; + std::map>> + int_metrics_; + std::map>> + float_metrics_; + std::map>> + double_metrics_; + + std::map>> + short_observers_; + std::map>> + int_observers_; + std::map>> + float_observers_; + std::map>> + double_observers_; + + std::unordered_set names_; + + std::string library_name_; + std::string library_version_; + + std::mutex metrics_lock_; + std::mutex observers_lock_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/meter_provider.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/meter_provider.h new file mode 100644 index 000000000..da218ceac --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/meter_provider.h @@ -0,0 +1,37 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_METRICS_PREVIEW + +# include "opentelemetry/_metrics/meter_provider.h" +# include "opentelemetry/nostd/shared_ptr.h" +# include "opentelemetry/sdk/_metrics/meter.h" + +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +class MeterProvider final : public opentelemetry::metrics::MeterProvider +{ +public: + /** + * Initialize a new meter provider + */ + explicit MeterProvider(std::string library_name = "", std::string library_version = "") noexcept; + + opentelemetry::nostd::shared_ptr GetMeter( + nostd::string_view library_name, + nostd::string_view library_version = "") noexcept override; + +private: + std::shared_ptr meter_; +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/processor.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/processor.h new file mode 100644 index 000000000..dd6c421fb --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/processor.h @@ -0,0 +1,38 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_METRICS_PREVIEW +# include "opentelemetry/_metrics/instrument.h" +# include "opentelemetry/nostd/string_view.h" +# include "opentelemetry/sdk/_metrics/record.h" +# include "opentelemetry/version.h" + +# include +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE + +namespace sdk +{ +namespace metrics +{ + +class MetricsProcessor +{ +public: + virtual ~MetricsProcessor() = default; + + virtual std::vector CheckpointSelf() noexcept = 0; + + virtual void FinishedCollection() noexcept = 0; + + virtual void process(opentelemetry::sdk::metrics::Record record) noexcept = 0; +}; + +} // namespace metrics +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/record.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/record.h new file mode 100644 index 000000000..a07c1595c --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/record.h @@ -0,0 +1,50 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_METRICS_PREVIEW + +# include +# include "opentelemetry/_metrics/instrument.h" +# include "opentelemetry/nostd/variant.h" +# include "opentelemetry/sdk/_metrics/aggregator/aggregator.h" + +OPENTELEMETRY_BEGIN_NAMESPACE + +namespace sdk +{ +namespace metrics +{ +using AggregatorVariant = nostd::variant>, + std::shared_ptr>, + std::shared_ptr>, + std::shared_ptr>>; +class Record +{ +public: + explicit Record(nostd::string_view name, + nostd::string_view description, + std::string labels, + AggregatorVariant aggregator) + { + name_ = std::string(name); + description_ = std::string(description); + labels_ = labels; + aggregator_ = aggregator; + } + + std::string GetName() { return name_; } + std::string GetDescription() { return description_; } + std::string GetLabels() { return labels_; } + AggregatorVariant GetAggregator() { return aggregator_; } + +private: + std::string name_; + std::string description_; + std::string labels_; + AggregatorVariant aggregator_; +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/sync_instruments.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/sync_instruments.h new file mode 100644 index 000000000..80d9b6092 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/sync_instruments.h @@ -0,0 +1,465 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_METRICS_PREVIEW + +# include +# include +# include +# include +# include + +# include "opentelemetry/_metrics/sync_instruments.h" +# include "opentelemetry/common/macros.h" +# include "opentelemetry/sdk/_metrics/aggregator/counter_aggregator.h" +# include "opentelemetry/sdk/_metrics/aggregator/min_max_sum_count_aggregator.h" +# include "opentelemetry/sdk/_metrics/instrument.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +# if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4250) // inheriting methods via dominance +# endif + +template +class BoundCounter final : public BoundSynchronousInstrument, + public opentelemetry::metrics::BoundCounter +{ + +public: + BoundCounter() = default; + + BoundCounter(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + bool enabled) + : BoundSynchronousInstrument( + name, + description, + unit, + enabled, + opentelemetry::metrics::InstrumentKind::Counter, + std::shared_ptr>(new CounterAggregator( + opentelemetry::metrics::InstrumentKind::Counter))) // Aggregator is chosen here + {} + + /* + * Add adds the value to the counter's sum. The labels are already linked to the instrument + * and are not specified. + * + * @param value the numerical representation of the metric being captured + * @param labels the set of labels, as key-value pairs + */ + virtual void add(T value) override + { + if (value < 0) + { +# if __EXCEPTIONS + throw std::invalid_argument("Counter instrument updates must be non-negative."); +# else + std::terminate(); +# endif + } + else + { + this->update(value); + } + } +}; + +template +class Counter final : public SynchronousInstrument, public opentelemetry::metrics::Counter +{ + +public: + Counter() = default; + + Counter(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + bool enabled) + : SynchronousInstrument(name, + description, + unit, + enabled, + opentelemetry::metrics::InstrumentKind::Counter) + {} + + /* + * Bind creates a bound instrument for this counter. The labels are + * associated with values recorded via subsequent calls to Record. + * + * @param labels the set of labels, as key-value pairs. + * @return a BoundCounter tied to the specified labels + */ + + virtual nostd::shared_ptr> bindCounter( + const opentelemetry::common::KeyValueIterable &labels) override + { + this->mu_.lock(); + std::string labelset = KvToString(labels); + if (boundInstruments_.find(labelset) == boundInstruments_.end()) + { + auto sp1 = nostd::shared_ptr>( + new BoundCounter(this->name_, this->description_, this->unit_, this->enabled_)); + boundInstruments_[labelset] = sp1; + this->mu_.unlock(); + return sp1; + } + else + { + boundInstruments_[labelset]->inc_ref(); + auto ret = boundInstruments_[labelset]; + this->mu_.unlock(); + return ret; + } + } + + /* + * Add adds the value to the counter's sum. The labels should contain + * the keys and values to be associated with this value. Counters only + * accept positive valued updates. + * + * @param value the numerical representation of the metric being captured + * @param labels the set of labels, as key-value pairs + */ + virtual void add(T value, const opentelemetry::common::KeyValueIterable &labels) override + { + if (value < 0) + { +# if __EXCEPTIONS + throw std::invalid_argument("Counter instrument updates must be non-negative."); +# else + std::terminate(); +# endif + } + else + { + auto sp = bindCounter(labels); + sp->update(value); + sp->unbind(); + } + } + + virtual std::vector GetRecords() override + { + this->mu_.lock(); + std::vector ret; + std::vector toDelete; + for (const auto &x : boundInstruments_) + { + if (x.second->get_ref() == 0) + { + toDelete.push_back(x.first); + } +# ifdef OPENTELEMETRY_RTTI_ENABLED + auto agg_ptr = dynamic_cast *>(x.second.get())->GetAggregator(); +# else + auto agg_ptr = static_cast *>(x.second.get())->GetAggregator(); +# endif + if (agg_ptr->is_updated()) + { + agg_ptr->checkpoint(); + ret.push_back(Record(x.second->GetName(), x.second->GetDescription(), x.first, agg_ptr)); + } + } + for (const auto &x : toDelete) + { + boundInstruments_.erase(x); + } + this->mu_.unlock(); + return ret; + } + + virtual void update(T val, const opentelemetry::common::KeyValueIterable &labels) override + { + add(val, labels); + } + + // A collection of the bound instruments created by this unbound instrument identified by their + // labels. + std::unordered_map>> + boundInstruments_; +}; + +template +class BoundUpDownCounter final : public BoundSynchronousInstrument, + virtual public opentelemetry::metrics::BoundUpDownCounter +{ + +public: + BoundUpDownCounter() = default; + + BoundUpDownCounter(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + bool enabled) + : BoundSynchronousInstrument(name, + description, + unit, + enabled, + opentelemetry::metrics::InstrumentKind::UpDownCounter, + std::shared_ptr>(new CounterAggregator( + opentelemetry::metrics::InstrumentKind::UpDownCounter))) + {} + + /* + * Add adds the value to the counter's sum. The labels are already linked to the instrument + * and are not specified. + * + * @param value the numerical representation of the metric being captured + * @param labels the set of labels, as key-value pairs + */ + virtual void add(T value) override { this->update(value); } +}; + +template +class UpDownCounter final : public SynchronousInstrument, + public opentelemetry::metrics::UpDownCounter +{ + +public: + UpDownCounter() = default; + + UpDownCounter(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + bool enabled) + : SynchronousInstrument(name, + description, + unit, + enabled, + opentelemetry::metrics::InstrumentKind::UpDownCounter) + {} + + /* + * Bind creates a bound instrument for this counter. The labels are + * associated with values recorded via subsequent calls to Record. + * + * @param labels the set of labels, as key-value pairs. + * @return a BoundIntCounter tied to the specified labels + */ + nostd::shared_ptr> bindUpDownCounter( + const opentelemetry::common::KeyValueIterable &labels) override + { + this->mu_.lock(); + std::string labelset = KvToString(labels); + if (boundInstruments_.find(labelset) == boundInstruments_.end()) + { + auto sp1 = nostd::shared_ptr>( + new BoundUpDownCounter(this->name_, this->description_, this->unit_, this->enabled_)); + boundInstruments_[labelset] = sp1; + this->mu_.unlock(); + return sp1; + } + else + { + boundInstruments_[labelset]->inc_ref(); + auto ret = boundInstruments_[labelset]; + this->mu_.unlock(); + return ret; + } + } + + /* + * Add adds the value to the counter's sum. The labels should contain + * the keys and values to be associated with this value. Counters only + * accept positive valued updates. + * + * @param value the numerical representation of the metric being captured + * @param labels the set of labels, as key-value pairs + */ + void add(T value, const opentelemetry::common::KeyValueIterable &labels) override + { + auto sp = bindUpDownCounter(labels); + sp->update(value); + sp->unbind(); + } + + virtual std::vector GetRecords() override + { + this->mu_.lock(); + std::vector ret; + std::vector toDelete; + for (const auto &x : boundInstruments_) + { + if (x.second->get_ref() == 0) + { + toDelete.push_back(x.first); + } +# ifdef OPENTELEMETRY_RTTI_ENABLED + auto agg_ptr = dynamic_cast *>(x.second.get())->GetAggregator(); +# else + auto agg_ptr = static_cast *>(x.second.get())->GetAggregator(); +# endif + if (agg_ptr->is_updated()) + { + agg_ptr->checkpoint(); + ret.push_back(Record(x.second->GetName(), x.second->GetDescription(), x.first, agg_ptr)); + } + } + for (const auto &x : toDelete) + { + boundInstruments_.erase(x); + } + this->mu_.unlock(); + return ret; + } + + virtual void update(T val, const opentelemetry::common::KeyValueIterable &labels) override + { + add(val, labels); + } + + std::unordered_map>> + boundInstruments_; +}; + +template +class BoundValueRecorder final : public BoundSynchronousInstrument, + public opentelemetry::metrics::BoundValueRecorder +{ + +public: + BoundValueRecorder() = default; + + BoundValueRecorder(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + bool enabled) + : BoundSynchronousInstrument( + name, + description, + unit, + enabled, + opentelemetry::metrics::InstrumentKind::ValueRecorder, + std::shared_ptr>(new MinMaxSumCountAggregator( + opentelemetry::metrics::InstrumentKind::ValueRecorder))) + {} + + /* + * Add adds the value to the counter's sum. The labels are already linked to the instrument + * and are not specified. + * + * @param value the numerical representation of the metric being captured + * @param labels the set of labels, as key-value pairs + */ + void record(T value) { this->update(value); } +}; + +template +class ValueRecorder final : public SynchronousInstrument, + public opentelemetry::metrics::ValueRecorder +{ + +public: + ValueRecorder() = default; + + ValueRecorder(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + bool enabled) + : SynchronousInstrument(name, + description, + unit, + enabled, + opentelemetry::metrics::InstrumentKind::ValueRecorder) + {} + + /* + * Bind creates a bound instrument for this counter. The labels are + * associated with values recorded via subsequent calls to Record. + * + * @param labels the set of labels, as key-value pairs. + * @return a BoundIntCounter tied to the specified labels + */ + nostd::shared_ptr> bindValueRecorder( + const opentelemetry::common::KeyValueIterable &labels) override + { + this->mu_.lock(); + std::string labelset = KvToString(labels); + if (boundInstruments_.find(labelset) == boundInstruments_.end()) + { + auto sp1 = nostd::shared_ptr>( + new BoundValueRecorder(this->name_, this->description_, this->unit_, this->enabled_)); + boundInstruments_[labelset] = sp1; + this->mu_.unlock(); + return sp1; + } + else + { + boundInstruments_[labelset]->inc_ref(); + auto ret = boundInstruments_[labelset]; + this->mu_.unlock(); + return ret; + } + } + + /* + * Add adds the value to the counter's sum. The labels should contain + * the keys and values to be associated with this value. Counters only + * accept positive valued updates. + * + * @param value the numerical representation of the metric being captured + * @param labels the set of labels, as key-value pairs + */ + void record(T value, const opentelemetry::common::KeyValueIterable &labels) override + { + auto sp = bindValueRecorder(labels); + sp->update(value); + sp->unbind(); + } + + virtual std::vector GetRecords() override + { + this->mu_.lock(); + std::vector ret; + std::vector toDelete; + for (const auto &x : boundInstruments_) + { + if (x.second->get_ref() == 0) + { + toDelete.push_back(x.first); + } +# ifdef OPENTELEMETRY_RTTI_ENABLED + auto agg_ptr = dynamic_cast *>(x.second.get())->GetAggregator(); +# else + auto agg_ptr = static_cast *>(x.second.get())->GetAggregator(); +# endif + if (agg_ptr->is_updated()) + { + agg_ptr->checkpoint(); + ret.push_back(Record(x.second->GetName(), x.second->GetDescription(), x.first, agg_ptr)); + } + } + for (const auto &x : toDelete) + { + boundInstruments_.erase(x); + } + this->mu_.unlock(); + return ret; + } + + virtual void update(T value, const opentelemetry::common::KeyValueIterable &labels) override + { + record(value, labels); + } + + std::unordered_map>> + boundInstruments_; +}; + +# if defined(_MSC_VER) +# pragma warning(pop) +# endif + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/ungrouped_processor.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/ungrouped_processor.h new file mode 100644 index 000000000..cc13ae52b --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/_metrics/ungrouped_processor.h @@ -0,0 +1,365 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_METRICS_PREVIEW + +# include + +# include "opentelemetry/common/macros.h" +# include "opentelemetry/sdk/_metrics/aggregator/counter_aggregator.h" +# include "opentelemetry/sdk/_metrics/aggregator/exact_aggregator.h" +# include "opentelemetry/sdk/_metrics/aggregator/gauge_aggregator.h" +# include "opentelemetry/sdk/_metrics/aggregator/histogram_aggregator.h" +# include "opentelemetry/sdk/_metrics/aggregator/min_max_sum_count_aggregator.h" +# include "opentelemetry/sdk/_metrics/aggregator/sketch_aggregator.h" +# include "opentelemetry/sdk/_metrics/processor.h" +# include "opentelemetry/sdk/_metrics/record.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE + +namespace sdk +{ + +namespace metrics +{ + +struct KeyStruct +{ + std::string name; + std::string description; + std::string labels; + opentelemetry::metrics::InstrumentKind ins_kind; + + // constructor + KeyStruct(std::string name, + std::string description, + std::string labels, + opentelemetry::metrics::InstrumentKind ins_kind) + { + this->name = name; + this->description = description; + this->labels = labels; + this->ins_kind = ins_kind; + } + + // operator== is required to compare keys in case of hash collision + bool operator==(const KeyStruct &p) const + { + return name == p.name && description == p.description && labels == p.labels && + ins_kind == p.ins_kind; + } +}; + +struct KeyStruct_Hash +{ + std::size_t operator()(const KeyStruct &keystruct) const + { + std::size_t name_size = keystruct.name.length(); + std::size_t desc_size = keystruct.description.length(); + std::size_t labels_size = keystruct.labels.length(); + std::size_t ins_size = (int)keystruct.ins_kind; + + return (name_size ^ desc_size ^ labels_size) + ins_size; + } +}; + +class UngroupedMetricsProcessor : public MetricsProcessor +{ +public: + explicit UngroupedMetricsProcessor(bool stateful); + + std::vector CheckpointSelf() noexcept override; + + virtual void FinishedCollection() noexcept override; + + virtual void process(opentelemetry::sdk::metrics::Record record) noexcept override; + +private: + bool stateful_; + std::unordered_map + batch_map_; + + /** + * get_instrument returns the instrument from the passed in AggregatorVariant. We have to + * unpack the variant then get the instrument from the Aggreagtor. + */ + opentelemetry::metrics::InstrumentKind get_instrument( + opentelemetry::sdk::metrics::AggregatorVariant aggregator) + { + if (nostd::holds_alternative>>( + aggregator)) + { + return nostd::get>>(aggregator) + ->get_instrument_kind(); + } + else if (nostd::holds_alternative< + std::shared_ptr>>(aggregator)) + { + return nostd::get>>(aggregator) + ->get_instrument_kind(); + } + else if (nostd::holds_alternative< + std::shared_ptr>>(aggregator)) + { + return nostd::get>>(aggregator) + ->get_instrument_kind(); + } + else if (nostd::holds_alternative< + std::shared_ptr>>(aggregator)) + { + return nostd::get>>( + aggregator) + ->get_instrument_kind(); + } + + return opentelemetry::metrics::InstrumentKind::Counter; + } + + /** + * aggregator_copy creates a copy of the aggregtor passed through process() for a + * stateful processor. For Sketch, Histogram and Exact we also need to pass in + * additional constructor values + */ + template + std::shared_ptr> aggregator_copy( + std::shared_ptr> aggregator) + { + auto ins_kind = aggregator->get_instrument_kind(); + auto agg_kind = aggregator->get_aggregator_kind(); + + switch (agg_kind) + { + case opentelemetry::sdk::metrics::AggregatorKind::Counter: + return std::shared_ptr>( + new opentelemetry::sdk::metrics::CounterAggregator(ins_kind)); + + case opentelemetry::sdk::metrics::AggregatorKind::MinMaxSumCount: + return std::shared_ptr>( + new opentelemetry::sdk::metrics::MinMaxSumCountAggregator(ins_kind)); + + case opentelemetry::sdk::metrics::AggregatorKind::Gauge: + return std::shared_ptr>( + new opentelemetry::sdk::metrics::GaugeAggregator(ins_kind)); + + case opentelemetry::sdk::metrics::AggregatorKind::Sketch: + return std::shared_ptr>( + new opentelemetry::sdk::metrics::SketchAggregator( + ins_kind, aggregator->get_error_bound(), aggregator->get_max_buckets())); + + case opentelemetry::sdk::metrics::AggregatorKind::Histogram: + return std::shared_ptr>( + new opentelemetry::sdk::metrics::HistogramAggregator(ins_kind, + aggregator->get_boundaries())); + + case opentelemetry::sdk::metrics::AggregatorKind::Exact: + return std::shared_ptr>( + new opentelemetry::sdk::metrics::ExactAggregator( + ins_kind, aggregator->get_quant_estimation())); + + default: + return std::shared_ptr>( + new opentelemetry::sdk::metrics::CounterAggregator(ins_kind)); + } + }; + + /** + * aggregator_for will return an Aggregator based off the instrument passed in. This should be + * the function that we assign Aggreagtors for instruments, but is currently unused in our + * pipeline. + */ + template + std::shared_ptr> aggregator_for( + opentelemetry::metrics::InstrumentKind ins_kind) + { + switch (ins_kind) + { + case opentelemetry::metrics::InstrumentKind::Counter: + return std::shared_ptr>( + new opentelemetry::sdk::metrics::CounterAggregator(ins_kind)); + + case opentelemetry::metrics::InstrumentKind::UpDownCounter: + return std::shared_ptr>( + new opentelemetry::sdk::metrics::CounterAggregator(ins_kind)); + + case opentelemetry::metrics::InstrumentKind::ValueRecorder: + return std::shared_ptr>( + new opentelemetry::sdk::metrics::MinMaxSumCountAggregator(ins_kind)); + + case opentelemetry::metrics::InstrumentKind::SumObserver: + return std::shared_ptr>( + new opentelemetry::sdk::metrics::CounterAggregator(ins_kind)); + + case opentelemetry::metrics::InstrumentKind::UpDownSumObserver: + return std::shared_ptr>( + new opentelemetry::sdk::metrics::CounterAggregator(ins_kind)); + + case opentelemetry::metrics::InstrumentKind::ValueObserver: + return std::shared_ptr>( + new opentelemetry::sdk::metrics::MinMaxSumCountAggregator(ins_kind)); + + default: + return std::shared_ptr>( + new opentelemetry::sdk::metrics::CounterAggregator(ins_kind)); + } + }; + + /** + * merge_aggreagtors takes in two shared pointers to aggregators of the same kind. + * We first need to dynamically cast to the actual Aggregator that is held in the + * Aggregator wrapper. Then we must get the underlying pointer from the shared + * pointer and merge them together. + */ + template + void merge_aggregators(std::shared_ptr> batch_agg, + std::shared_ptr> record_agg) + { + auto agg_kind = batch_agg->get_aggregator_kind(); + if (agg_kind == opentelemetry::sdk::metrics::AggregatorKind::Counter) + { +# ifdef OPENTELEMETRY_RTTI_ENABLED + std::shared_ptr> temp_batch_agg_counter = + std::dynamic_pointer_cast>(batch_agg); + + std::shared_ptr> temp_record_agg_counter = + std::dynamic_pointer_cast>(record_agg); +# else + std::shared_ptr> temp_batch_agg_counter = + std::static_pointer_cast>(batch_agg); + + std::shared_ptr> temp_record_agg_counter = + std::static_pointer_cast>(record_agg); +# endif + auto temp_batch_agg_raw_counter = temp_batch_agg_counter.get(); + auto temp_record_agg_raw_counter = temp_record_agg_counter.get(); + + temp_batch_agg_raw_counter->merge(*temp_record_agg_raw_counter); + } + else if (agg_kind == opentelemetry::sdk::metrics::AggregatorKind::MinMaxSumCount) + { +# ifdef OPENTELEMETRY_RTTI_ENABLED + std::shared_ptr> + temp_batch_agg_mmsc = + std::dynamic_pointer_cast>( + batch_agg); + + std::shared_ptr> + temp_record_agg_mmsc = + std::dynamic_pointer_cast>( + record_agg); +# else + std::shared_ptr> + temp_batch_agg_mmsc = + std::static_pointer_cast>( + batch_agg); + + std::shared_ptr> + temp_record_agg_mmsc = + std::static_pointer_cast>( + record_agg); +# endif + + auto temp_batch_agg_raw_mmsc = temp_batch_agg_mmsc.get(); + auto temp_record_agg_raw_mmsc = temp_record_agg_mmsc.get(); + + temp_batch_agg_raw_mmsc->merge(*temp_record_agg_raw_mmsc); + } + else if (agg_kind == opentelemetry::sdk::metrics::AggregatorKind::Gauge) + { +# ifdef OPENTELEMETRY_RTTI_ENABLED + std::shared_ptr> temp_batch_agg_gauge = + std::dynamic_pointer_cast>(batch_agg); + + std::shared_ptr> temp_record_agg_gauge = + std::dynamic_pointer_cast>(record_agg); +# else + std::shared_ptr> temp_batch_agg_gauge = + std::static_pointer_cast>(batch_agg); + + std::shared_ptr> temp_record_agg_gauge = + std::static_pointer_cast>(record_agg); +# endif + + auto temp_batch_agg_raw_gauge = temp_batch_agg_gauge.get(); + auto temp_record_agg_raw_gauge = temp_record_agg_gauge.get(); + + temp_batch_agg_raw_gauge->merge(*temp_record_agg_raw_gauge); + } + else if (agg_kind == opentelemetry::sdk::metrics::AggregatorKind::Sketch) + { +# ifdef OPENTELEMETRY_RTTI_ENABLED + std::shared_ptr> temp_batch_agg_sketch = + std::dynamic_pointer_cast>(batch_agg); + + std::shared_ptr> temp_record_agg_sketch = + std::dynamic_pointer_cast>(record_agg); +# else + std::shared_ptr> temp_batch_agg_sketch = + std::static_pointer_cast>(batch_agg); + + std::shared_ptr> temp_record_agg_sketch = + std::static_pointer_cast>(record_agg); +# endif + auto temp_batch_agg_raw_sketch = temp_batch_agg_sketch.get(); + auto temp_record_agg_raw_sketch = temp_record_agg_sketch.get(); + + temp_batch_agg_raw_sketch->merge(*temp_record_agg_raw_sketch); + } + else if (agg_kind == opentelemetry::sdk::metrics::AggregatorKind::Histogram) + { +# ifdef OPENTELEMETRY_RTTI_ENABLED + std::shared_ptr> + temp_batch_agg_histogram = + std::dynamic_pointer_cast>( + batch_agg); + + std::shared_ptr> + temp_record_agg_histogram = + std::dynamic_pointer_cast>( + record_agg); +# else + std::shared_ptr> + temp_batch_agg_histogram = + std::static_pointer_cast>( + batch_agg); + + std::shared_ptr> + temp_record_agg_histogram = + std::static_pointer_cast>( + record_agg); +# endif + + auto temp_batch_agg_raw_histogram = temp_batch_agg_histogram.get(); + auto temp_record_agg_raw_histogram = temp_record_agg_histogram.get(); + + temp_batch_agg_raw_histogram->merge(*temp_record_agg_raw_histogram); + } + else if (agg_kind == opentelemetry::sdk::metrics::AggregatorKind::Exact) + { +# ifdef OPENTELEMETRY_RTTI_ENABLED + std::shared_ptr> temp_batch_agg_exact = + std::dynamic_pointer_cast>(batch_agg); + + std::shared_ptr> temp_record_agg_exact = + std::dynamic_pointer_cast>(record_agg); +# else + std::shared_ptr> temp_batch_agg_exact = + std::static_pointer_cast>(batch_agg); + + std::shared_ptr> temp_record_agg_exact = + std::static_pointer_cast>(record_agg); +# endif + + auto temp_batch_agg_raw_exact = temp_batch_agg_exact.get(); + auto temp_record_agg_raw_exact = temp_record_agg_exact.get(); + + temp_batch_agg_raw_exact->merge(*temp_record_agg_raw_exact); + } + } +}; +} // namespace metrics +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/atomic_shared_ptr.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/atomic_shared_ptr.h new file mode 100644 index 000000000..07e19ac0f --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/atomic_shared_ptr.h @@ -0,0 +1,62 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ +/** + * A wrapper to provide atomic shared pointers. + * + * This wrapper relies on a mutex for gcc 4.8, and specializations of + * std::atomic_store and std::atomic_load for all other instances. + */ +#if (__GNUC__ == 4 && (__GNUC_MINOR__ >= 8)) +template +class AtomicSharedPtr +{ +public: + explicit AtomicSharedPtr(std::shared_ptr ptr) noexcept : ptr_{std::move(ptr)} {} + + void store(const std::shared_ptr &other) noexcept + { + std::lock_guard lock_guard{mu_}; + ptr_ = other; + } + + std::shared_ptr load() const noexcept + { + std::lock_guard lock_guard{mu_}; + return ptr_; + } + +private: + std::shared_ptr ptr_; + mutable std::mutex mu_; +}; +#else +template +class AtomicSharedPtr +{ +public: + explicit AtomicSharedPtr(std::shared_ptr ptr) noexcept : ptr_{std::move(ptr)} {} + + void store(const std::shared_ptr &other) noexcept { std::atomic_store(&ptr_, other); } + + std::shared_ptr load() const noexcept { return std::atomic_load(&ptr_); } + +private: + std::shared_ptr ptr_; +}; +#endif +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/atomic_unique_ptr.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/atomic_unique_ptr.h new file mode 100644 index 000000000..5945df98c --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/atomic_unique_ptr.h @@ -0,0 +1,87 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ +/** + * An owning pointer similar to std::unique_ptr but with methods for atomic + * operations. + */ +template +class AtomicUniquePtr +{ +public: + AtomicUniquePtr() noexcept {} + + explicit AtomicUniquePtr(std::unique_ptr &&other) noexcept : ptr_(other.release()) {} + + ~AtomicUniquePtr() noexcept { Reset(); } + + T &operator*() const noexcept { return *Get(); } + + T *operator->() const noexcept { return Get(); } + + /** + * @return the underly pointer managed. + */ + T *Get() const noexcept { return ptr_; } + + /** + * @return true if the pointer is null + */ + bool IsNull() const noexcept { return ptr_.load() == nullptr; } + + /** + * Atomically swap the pointer only if it's null. + * @param owner the pointer to swap with + * @return true if the swap was successful + */ + bool SwapIfNull(std::unique_ptr &owner) noexcept + { + auto ptr = owner.get(); + T *expected = nullptr; + auto was_successful = ptr_.compare_exchange_weak(expected, ptr, std::memory_order_release, + std::memory_order_relaxed); + if (was_successful) + { + owner.release(); + return true; + } + return false; + } + + /** + * Atomically swap the pointer with another. + * @param ptr the pointer to swap with + */ + void Swap(std::unique_ptr &other) noexcept { other.reset(ptr_.exchange(other.release())); } + + /** + * Set the pointer to a new value and delete the current value if non-null. + * @param ptr the new pointer value to set + */ + void Reset(T *ptr = nullptr) noexcept + { + ptr = ptr_.exchange(ptr); + if (ptr != nullptr) + { + delete ptr; + } + } + +private: + std::atomic ptr_{nullptr}; +}; +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/attribute_utils.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/attribute_utils.h new file mode 100644 index 000000000..68b09f044 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/attribute_utils.h @@ -0,0 +1,199 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include +#include +#include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/common/key_value_iterable_view.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ +/** + * A counterpart to AttributeValue that makes sure a value is owned. This + * replaces all non-owning references with owned copies. + * + * The following types are not currently supported by the OpenTelemetry + * specification, but reserved for future use: + * - uint64_t + * - std::vector + * - std::vector + */ +using OwnedAttributeValue = nostd::variant, + std::vector, + std::vector, + std::vector, + std::vector, + std::vector, + uint64_t, + std::vector, + std::vector>; + +enum OwnedAttributeType +{ + kTypeBool, + kTypeInt, + kTypeUInt, + kTypeInt64, + kTypeDouble, + kTypeString, + kTypeSpanBool, + kTypeSpanInt, + kTypeSpanUInt, + kTypeSpanInt64, + kTypeSpanDouble, + kTypeSpanString, + kTypeUInt64, + kTypeSpanUInt64, + kTypeSpanByte +}; + +/** + * Creates an owned copy (OwnedAttributeValue) of a non-owning AttributeValue. + */ +struct AttributeConverter +{ + OwnedAttributeValue operator()(bool v) { return OwnedAttributeValue(v); } + OwnedAttributeValue operator()(int32_t v) { return OwnedAttributeValue(v); } + OwnedAttributeValue operator()(uint32_t v) { return OwnedAttributeValue(v); } + OwnedAttributeValue operator()(int64_t v) { return OwnedAttributeValue(v); } + OwnedAttributeValue operator()(uint64_t v) { return OwnedAttributeValue(v); } + OwnedAttributeValue operator()(double v) { return OwnedAttributeValue(v); } + OwnedAttributeValue operator()(nostd::string_view v) + { + return OwnedAttributeValue(std::string(v)); + } + OwnedAttributeValue operator()(std::string v) { return OwnedAttributeValue(v); } + OwnedAttributeValue operator()(const char *v) { return OwnedAttributeValue(std::string(v)); } + OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } + OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } + OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } + OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } + OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } + OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } + OwnedAttributeValue operator()(nostd::span v) { return convertSpan(v); } + OwnedAttributeValue operator()(nostd::span v) + { + return convertSpan(v); + } + + template + OwnedAttributeValue convertSpan(nostd::span vals) + { + const std::vector copy(vals.begin(), vals.end()); + return OwnedAttributeValue(std::move(copy)); + } +}; + +/** + * Class for storing attributes. + */ +class AttributeMap : public std::unordered_map +{ +public: + // Contruct empty attribute map + AttributeMap() : std::unordered_map(){}; + + // Contruct attribute map and populate with attributes + AttributeMap(const opentelemetry::common::KeyValueIterable &attributes) : AttributeMap() + { + attributes.ForEachKeyValue( + [&](nostd::string_view key, opentelemetry::common::AttributeValue value) noexcept { + SetAttribute(key, value); + return true; + }); + } + + // Construct map from initializer list by applying `SetAttribute` transform for every attribute + AttributeMap( + std::initializer_list> + attributes) + : AttributeMap() + { + for (auto &kv : attributes) + { + SetAttribute(kv.first, kv.second); + } + } + + // Returns a reference to this map + const std::unordered_map &GetAttributes() const noexcept + { + return (*this); + } + + // Convert non-owning key-value to owning std::string(key) and OwnedAttributeValue(value) + void SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept + { + (*this)[std::string(key)] = nostd::visit(converter_, value); + } + +private: + AttributeConverter converter_; +}; + +/** + * Class for storing attributes. + */ +class OrderedAttributeMap : public std::map +{ +public: + // Contruct empty attribute map + OrderedAttributeMap() : std::map(){}; + + // Contruct attribute map and populate with attributes + OrderedAttributeMap(const opentelemetry::common::KeyValueIterable &attributes) + : OrderedAttributeMap() + { + attributes.ForEachKeyValue( + [&](nostd::string_view key, opentelemetry::common::AttributeValue value) noexcept { + SetAttribute(key, value); + return true; + }); + } + + // Construct map from initializer list by applying `SetAttribute` transform for every attribute + OrderedAttributeMap( + std::initializer_list> + attributes) + : OrderedAttributeMap() + { + for (auto &kv : attributes) + { + SetAttribute(kv.first, kv.second); + } + } + + // Returns a reference to this map + const std::map &GetAttributes() const noexcept + { + return (*this); + } + + // Convert non-owning key-value to owning std::string(key) and OwnedAttributeValue(value) + void SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept + { + (*this)[std::string(key)] = nostd::visit(converter_, value); + } + +private: + AttributeConverter converter_; +}; + +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/attributemap_hash.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/attributemap_hash.h new file mode 100644 index 000000000..573f57eb1 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/attributemap_hash.h @@ -0,0 +1,62 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include "opentelemetry/sdk/common/attribute_utils.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ + +template +inline void GetHashForAttributeValue(size_t &seed, const T arg) +{ + std::hash hasher; + // reference - + // https://www.boost.org/doc/libs/1_37_0/doc/html/hash/reference.html#boost.hash_combine + seed ^= hasher(arg) + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} + +template +inline void GetHashForAttributeValue(size_t &seed, const std::vector &arg) +{ + for (auto v : arg) + { + GetHashForAttributeValue(seed, v); + } +} + +struct GetHashForAttributeValueVisitor +{ + GetHashForAttributeValueVisitor(size_t &seed) : seed_(seed) {} + template + void operator()(T &v) + { + GetHashForAttributeValue(seed_, v); + } + size_t &seed_; +}; + +// Calculate hash of keys and values of attribute map +inline size_t GetHashForAttributeMap(const OrderedAttributeMap &attribute_map) +{ + size_t seed = 0UL; + for (auto &kv : attribute_map) + { + std::hash hasher; + // reference - + // https://www.boost.org/doc/libs/1_37_0/doc/html/hash/reference.html#boost.hash_combine + seed ^= hasher(kv.first) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + nostd::visit(GetHashForAttributeValueVisitor(seed), kv.second); + } + return seed; +} + +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/circular_buffer.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/circular_buffer.h new file mode 100644 index 000000000..6af900183 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/circular_buffer.h @@ -0,0 +1,186 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include + +#include "opentelemetry/sdk/common/atomic_unique_ptr.h" +#include "opentelemetry/sdk/common/circular_buffer_range.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ +/* + * A lock-free circular buffer that supports multiple concurrent producers + * and a single consumer. + */ +template +class CircularBuffer +{ +public: + explicit CircularBuffer(size_t max_size) + : data_{new AtomicUniquePtr[max_size + 1]}, capacity_{max_size + 1} + {} + + /** + * @return a range of the elements in the circular buffer + * + * Note: This method must only be called from the consumer thread. + */ + CircularBufferRange> Peek() const noexcept + { + return const_cast(this)->PeekImpl(); + } + + /** + * Consume elements from the circular buffer's tail. + * @param n the number of elements to consume + * @param callback the callback to invoke with an AtomicUniquePtr to each + * consumed element. + * + * Note: The callback must set the passed AtomicUniquePtr to null. + * + * Note: This method must only be called from the consumer thread. + */ + template + void Consume(size_t n, Callback callback) noexcept + { + assert(n <= static_cast(head_ - tail_)); + auto range = PeekImpl().Take(n); + static_assert(noexcept(callback(range)), "callback not allowed to throw"); + tail_ += n; + callback(range); + } + + /** + * Consume elements from the circular buffer's tail. + * @param n the number of elements to consume + * + * Note: This method must only be called from the consumer thread. + */ + void Consume(size_t n) noexcept + { + Consume(n, [](CircularBufferRange> &range) noexcept { + range.ForEach([](AtomicUniquePtr &ptr) noexcept { + ptr.Reset(); + return true; + }); + }); + } + + /** + * Adds an element into the circular buffer. + * @param ptr a pointer to the element to add + * @return true if the element was successfully added; false, otherwise. + */ + bool Add(std::unique_ptr &ptr) noexcept + { + while (true) + { + uint64_t tail = tail_; + uint64_t head = head_; + + // The circular buffer is full, so return false. + if (head - tail >= capacity_ - 1) + { + return false; + } + + uint64_t head_index = head % capacity_; + if (data_[head_index].SwapIfNull(ptr)) + { + auto new_head = head + 1; + auto expected_head = head; + if (head_.compare_exchange_weak(expected_head, new_head, std::memory_order_release, + std::memory_order_relaxed)) + { + // free the swapped out value + ptr.reset(); + + return true; + } + + // If we reached this point (unlikely), it means that between the last + // iteration elements were added and then consumed from the circular + // buffer, so we undo the swap and attempt to add again. + data_[head_index].Swap(ptr); + } + } + return true; + } + + /** + * Clear the circular buffer. + * + * Note: This method must only be called from the consumer thread. + */ + void Clear() noexcept { Consume(size()); } + + /** + * @return the maximum number of bytes that can be stored in the buffer. + */ + size_t max_size() const noexcept { return capacity_ - 1; } + + /** + * @return true if the buffer is empty. + */ + bool empty() const noexcept { return head_ == tail_; } + + /** + * @return the number of bytes stored in the circular buffer. + * + * Note: this method will only return a correct snapshot of the size if called + * from the consumer thread. + */ + size_t size() const noexcept + { + uint64_t tail = tail_; + uint64_t head = head_; + assert(tail <= head); + return head - tail; + } + + /** + * @return the number of elements consumed from the circular buffer. + */ + uint64_t consumption_count() const noexcept { return tail_; } + + /** + * @return the number of elements added to the circular buffer. + */ + uint64_t production_count() const noexcept { return head_; } + +private: + std::unique_ptr[]> data_; + size_t capacity_; + std::atomic head_{0}; + std::atomic tail_{0}; + + CircularBufferRange> PeekImpl() noexcept + { + uint64_t tail_index = tail_ % capacity_; + uint64_t head_index = head_ % capacity_; + if (head_index == tail_index) + { + return {}; + } + auto data = data_.get(); + if (tail_index < head_index) + { + return CircularBufferRange>{nostd::span>{ + data + tail_index, static_cast(head_index - tail_index)}}; + } + return {nostd::span>{data + tail_index, + static_cast(capacity_ - tail_index)}, + nostd::span>{data, static_cast(head_index)}}; + } +}; +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/circular_buffer_range.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/circular_buffer_range.h new file mode 100644 index 000000000..9ef3b66be --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/circular_buffer_range.h @@ -0,0 +1,93 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include +#include + +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ +/** + * A non-owning view into a range of elements in a circular buffer. + */ +template +class CircularBufferRange +{ +public: + CircularBufferRange() noexcept = default; + + explicit CircularBufferRange(nostd::span first) noexcept : first_{first} {} + + CircularBufferRange(nostd::span first, nostd::span second) noexcept + : first_{first}, second_{second} + {} + + operator CircularBufferRange() const noexcept { return {first_, second_}; } + + /** + * Iterate over the elements in the range. + * @param callback the callback to call for each element + * @return true if we iterated over all elements + */ + template + bool ForEach(Callback callback) const + noexcept(noexcept(std::declval()(std::declval()))) + { + for (auto &value : first_) + { + if (!callback(value)) + { + return false; + } + } + for (auto &value : second_) + { + if (!callback(value)) + { + return false; + } + } + return true; + } + + /** + * @return the number of elements in the range + */ + size_t size() const noexcept { return first_.size() + second_.size(); } + + /** + * @return true if the range is empty + */ + bool empty() const noexcept { return first_.empty(); } + + /** + * Return a subrange taken from the start of this range. + * @param n the number of element to take in the subrange + * @return a subrange of the first n elements in this range + */ + CircularBufferRange Take(size_t n) const noexcept + { + assert(n <= size()); + if (first_.size() >= n) + { + return CircularBufferRange{nostd::span{first_.data(), n}}; + } + return {first_, nostd::span{second_.data(), n - first_.size()}}; + } + +private: + nostd::span first_; + nostd::span second_; +}; +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/empty_attributes.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/empty_attributes.h new file mode 100644 index 000000000..4f740bf10 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/empty_attributes.h @@ -0,0 +1,34 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/common/key_value_iterable_view.h" +#include "opentelemetry/common/macros.h" + +#include +#include +#include +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +/** + * Maintain a static empty array of pairs that represents empty (default) attributes. + * This helps to avoid constructing a new empty container every time a call is made + * with default attributes. + */ +OPENTELEMETRY_MAYBE_UNUSED static const opentelemetry::common::KeyValueIterableView< + std::array, 0>> + &GetEmptyAttributes() noexcept +{ + static const std::array, 0> array{}; + static const opentelemetry::common::KeyValueIterableView< + std::array, 0>> + kEmptyAttributes(array); + + return kEmptyAttributes; +} +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/env_variables.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/env_variables.h new file mode 100644 index 000000000..be955ee86 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/env_variables.h @@ -0,0 +1,53 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ + +#if defined(_MSC_VER) +inline int setenv(const char *name, const char *value, int) +{ + return _putenv_s(name, value); +} + +inline int unsetenv(const char *name) +{ + return setenv(name, "", 1); +} +#endif + +// Returns the env variable set. +inline const std::string GetEnvironmentVariable(const char *env_var_name) +{ +#if !defined(NO_GETENV) + const char *endpoint_from_env = nullptr; +# if defined(_MSC_VER) + // avoid calling std::getenv which is deprecated in MSVC. + size_t required_size = 0; + getenv_s(&required_size, nullptr, 0, env_var_name); + std::unique_ptr endpoint_buffer; + if (required_size > 0) + { + endpoint_buffer = std::unique_ptr{new char[required_size]}; + getenv_s(&required_size, endpoint_buffer.get(), required_size, env_var_name); + endpoint_from_env = endpoint_buffer.get(); + } +# else + endpoint_from_env = std::getenv(env_var_name); +# endif // defined(_MSC_VER) + return endpoint_from_env == nullptr ? std::string{} : std::string{endpoint_from_env}; +#else + return std::string{}; +#endif // !defined(NO_GETENV) +} +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/exporter_utils.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/exporter_utils.h new file mode 100644 index 000000000..091f481f9 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/exporter_utils.h @@ -0,0 +1,34 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ +/** + * ExportResult is returned as result of exporting a batch of Records. + */ +enum class ExportResult +{ + // Batch was exported successfully. + kSuccess = 0, + + // Batch exporting failed, caller must not retry exporting the same batch + // and the batch must be dropped. + kFailure = 1, + + // The collection does not have enough space to receive the export batch. + kFailureFull = 2, + + // The export() function was passed an invalid argument. + kFailureInvalidArgument = 3 +}; + +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/global_log_handler.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/global_log_handler.h new file mode 100644 index 000000000..99cf48b82 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/common/global_log_handler.h @@ -0,0 +1,222 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include + +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/sdk/common/attribute_utils.h" +#include "opentelemetry/version.h" + +#define OTEL_INTERNAL_LOG_LEVEL_ERROR 0 +#define OTEL_INTERNAL_LOG_LEVEL_WARN 1 +#define OTEL_INTERNAL_LOG_LEVEL_INFO 2 +#define OTEL_INTERNAL_LOG_LEVEL_DEBUG 3 +#ifndef OTEL_INTERNAL_LOG_LEVEL +// DEBUG by default, we can change log level on runtime +# define OTEL_INTERNAL_LOG_LEVEL OTEL_INTERNAL_LOG_LEVEL_DEBUG +#endif + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ +namespace internal_log +{ + +enum class LogLevel +{ + Error = 0, + Warning, + Info, + Debug +}; + +inline std::string LevelToString(LogLevel level) +{ + switch (level) + { + case LogLevel::Error: + return "Error"; + case LogLevel::Warning: + return "Warning"; + case LogLevel::Info: + return "Info"; + case LogLevel::Debug: + return "Debug"; + } + return {}; +} + +class LogHandler +{ +public: + virtual ~LogHandler(); + + virtual void Handle(LogLevel level, + const char *file, + int line, + const char *msg, + const sdk::common::AttributeMap &attributes) noexcept = 0; +}; + +class DefaultLogHandler : public LogHandler +{ +public: + void Handle(LogLevel level, + const char *file, + int line, + const char *msg, + const sdk::common::AttributeMap &attributes) noexcept override; +}; + +class NoopLogHandler : public LogHandler +{ +public: + void Handle(LogLevel level, + const char *file, + int line, + const char *msg, + const sdk::common::AttributeMap &error_attributes) noexcept override; +}; + +/** + * Stores the singleton global LogHandler. + */ +class GlobalLogHandler +{ +public: + /** + * Returns the singleton LogHandler. + * + * By default, a default LogHandler is returned. + */ + static inline const nostd::shared_ptr &GetLogHandler() noexcept + { + return GetHandlerAndLevel().first; + } + + /** + * Changes the singleton LogHandler. + * This should be called once at the start of application before creating any Provider + * instance. + */ + static inline void SetLogHandler(nostd::shared_ptr eh) noexcept + { + GetHandlerAndLevel().first = eh; + } + + /** + * Returns the singleton log level. + * + * By default, a default log level is returned. + */ + static inline LogLevel GetLogLevel() noexcept { return GetHandlerAndLevel().second; } + + /** + * Changes the singleton Log level. + * This should be called once at the start of application before creating any Provider + * instance. + */ + static inline void SetLogLevel(LogLevel level) noexcept { GetHandlerAndLevel().second = level; } + +private: + static std::pair, LogLevel> &GetHandlerAndLevel() noexcept; +}; + +} // namespace internal_log +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE + +/** + * We can not decide the destroying order of signaltons. + * Which means, the destructors of other singletons (GlobalLogHandler,TracerProvider and etc.) + * may be called after destroying of global LogHandler and use OTEL_INTERNAL_LOG_* in it.We can do + * nothing but ignore the log in this situation. + */ +#define OTEL_INTERNAL_LOG_DISPATCH(level, message, attributes) \ + do \ + { \ + using opentelemetry::sdk::common::internal_log::GlobalLogHandler; \ + using opentelemetry::sdk::common::internal_log::LogHandler; \ + if (level > GlobalLogHandler::GetLogLevel()) \ + { \ + break; \ + } \ + const opentelemetry::nostd::shared_ptr &log_handler = \ + GlobalLogHandler::GetLogHandler(); \ + if (!log_handler) \ + { \ + break; \ + } \ + std::stringstream tmp_stream; \ + tmp_stream << message; \ + log_handler->Handle(level, __FILE__, __LINE__, tmp_stream.str().c_str(), attributes); \ + } while (false); + +#define OTEL_INTERNAL_LOG_GET_3RD_ARG(arg1, arg2, arg3, ...) arg3 + +#if OTEL_INTERNAL_LOG_LEVEL >= OTEL_INTERNAL_LOG_LEVEL_ERROR +# define OTEL_INTERNAL_LOG_ERROR_1_ARGS(message) \ + OTEL_INTERNAL_LOG_DISPATCH(opentelemetry::sdk::common::internal_log::LogLevel::Error, message, \ + {}) +# define OTEL_INTERNAL_LOG_ERROR_2_ARGS(message, attributes) \ + OTEL_INTERNAL_LOG_DISPATCH(opentelemetry::sdk::common::internal_log::LogLevel::Error, message, \ + attributes) +# define OTEL_INTERNAL_LOG_ERROR_MACRO(...) \ + OTEL_INTERNAL_LOG_GET_3RD_ARG(__VA_ARGS__, OTEL_INTERNAL_LOG_ERROR_2_ARGS, \ + OTEL_INTERNAL_LOG_ERROR_1_ARGS) +# define OTEL_INTERNAL_LOG_ERROR(...) OTEL_INTERNAL_LOG_ERROR_MACRO(__VA_ARGS__)(__VA_ARGS__) +#else +# define OTEL_INTERNAL_LOG_ERROR(...) +#endif + +#if OTEL_INTERNAL_LOG_LEVEL >= OTEL_INTERNAL_LOG_LEVEL_WARN +# define OTEL_INTERNAL_LOG_WARN_1_ARGS(message) \ + OTEL_INTERNAL_LOG_DISPATCH(opentelemetry::sdk::common::internal_log::LogLevel::Warning, \ + message, {}) +# define OTEL_INTERNAL_LOG_WARN_2_ARGS(message, attributes) \ + OTEL_INTERNAL_LOG_DISPATCH(opentelemetry::sdk::common::internal_log::LogLevel::Warning, \ + message, attributes) +# define OTEL_INTERNAL_LOG_WARN_MACRO(...) \ + OTEL_INTERNAL_LOG_GET_3RD_ARG(__VA_ARGS__, OTEL_INTERNAL_LOG_WARN_2_ARGS, \ + OTEL_INTERNAL_LOG_WARN_1_ARGS) +# define OTEL_INTERNAL_LOG_WARN(...) OTEL_INTERNAL_LOG_WARN_MACRO(__VA_ARGS__)(__VA_ARGS__) +#else +# define OTEL_INTERNAL_LOG_ERROR(...) +#endif + +#if OTEL_INTERNAL_LOG_LEVEL >= OTEL_INTERNAL_LOG_LEVEL_DEBUG +# define OTEL_INTERNAL_LOG_DEBUG_1_ARGS(message) \ + OTEL_INTERNAL_LOG_DISPATCH(opentelemetry::sdk::common::internal_log::LogLevel::Debug, message, \ + {}) +# define OTEL_INTERNAL_LOG_DEBUG_2_ARGS(message, attributes) \ + OTEL_INTERNAL_LOG_DISPATCH(opentelemetry::sdk::common::internal_log::LogLevel::Debug, message, \ + attributes) +# define OTEL_INTERNAL_LOG_DEBUG_MACRO(...) \ + OTEL_INTERNAL_LOG_GET_3RD_ARG(__VA_ARGS__, OTEL_INTERNAL_LOG_DEBUG_2_ARGS, \ + OTEL_INTERNAL_LOG_DEBUG_1_ARGS) +# define OTEL_INTERNAL_LOG_DEBUG(...) OTEL_INTERNAL_LOG_DEBUG_MACRO(__VA_ARGS__)(__VA_ARGS__) +#else +# define OTEL_INTERNAL_LOG_DEBUG(...) +#endif + +#if OTEL_INTERNAL_LOG_LEVEL >= OTEL_INTERNAL_LOG_LEVEL_INFO +# define OTEL_INTERNAL_LOG_INFO_1_ARGS(message) \ + OTEL_INTERNAL_LOG_DISPATCH(opentelemetry::sdk::common::internal_log::LogLevel::Info, message, \ + {}) +# define OTEL_INTERNAL_LOG_INFO_2_ARGS(message, attributes) \ + OTEL_INTERNAL_LOG_DISPATCH(opentelemetry::sdk::common::internal_log::LogLevel::Info, message, \ + attributes) +# define OTEL_INTERNAL_LOG_INFO_MACRO(...) \ + OTEL_INTERNAL_LOG_GET_3RD_ARG(__VA_ARGS__, OTEL_INTERNAL_LOG_INFO_2_ARGS, \ + OTEL_INTERNAL_LOG_INFO_1_ARGS) +# define OTEL_INTERNAL_LOG_INFO(...) OTEL_INTERNAL_LOG_INFO_MACRO(__VA_ARGS__)(__VA_ARGS__) +#else +# define OTEL_INTERNAL_LOG_INFO(...) +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h new file mode 100644 index 000000000..20f82a5d1 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h @@ -0,0 +1,96 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/nostd/unique_ptr.h" +#include "opentelemetry/version.h" + +#include +#include + +OPENTELEMETRY_BEGIN_NAMESPACE + +namespace sdk +{ +namespace instrumentationlibrary +{ + +class InstrumentationLibrary +{ +public: + InstrumentationLibrary(const InstrumentationLibrary &) = default; + + /** + * Returns a newly created InstrumentationLibrary with the specified library name and version. + * @param name name of the instrumentation library. + * @param version version of the instrumentation library. + * @param schema_url schema url of the telemetry emitted by the library. + * @returns the newly created InstrumentationLibrary. + */ + static nostd::unique_ptr Create(nostd::string_view name, + nostd::string_view version = "", + nostd::string_view schema_url = "") + { + return nostd::unique_ptr( + new InstrumentationLibrary{name, version, schema_url}); + } + + std::size_t HashCode() const noexcept { return hash_code_; } + + /** + * Compare 2 instrumentation libraries. + * @param other the instrumentation library to compare to. + * @returns true if the 2 instrumentation libraries are equal, false otherwise. + */ + bool operator==(const InstrumentationLibrary &other) const + { + return equal(other.name_, other.version_, other.schema_url_); + } + + /** + * Check whether the instrumentation library has given name and version. + * This could be used to check version equality and avoid heap allocation. + * @param name name of the instrumentation library to compare. + * @param version version of the instrumentatoin library to compare. + * @param schema_url schema url of the telemetry emitted by the library. + * @returns true if name and version in this instrumentation library are equal with the given name + * and version. + */ + bool equal(const nostd::string_view name, + const nostd::string_view version, + const nostd::string_view schema_url = "") const + { + return this->name_ == name && this->version_ == version && this->schema_url_ == schema_url; + } + + const std::string &GetName() const { return name_; } + const std::string &GetVersion() const { return version_; } + const std::string &GetSchemaURL() const { return schema_url_; } + +private: + InstrumentationLibrary(nostd::string_view name, + nostd::string_view version, + nostd::string_view schema_url = "") + : name_(name), version_(version), schema_url_(schema_url) + { + std::string hash_data; + hash_data.reserve(name_.size() + version_.size() + schema_url_.size()); + hash_data += name_; + hash_data += version_; + hash_data += schema_url_; + hash_code_ = std::hash{}(hash_data); + } + +private: + std::string name_; + std::string version_; + std::string schema_url_; + std::size_t hash_code_; +}; + +} // namespace instrumentationlibrary +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/batch_log_processor.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/batch_log_processor.h new file mode 100644 index 000000000..1b6d443c8 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/batch_log_processor.h @@ -0,0 +1,128 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/common/circular_buffer.h" +# include "opentelemetry/sdk/logs/exporter.h" +# include "opentelemetry/sdk/logs/processor.h" + +# include +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ + +namespace logs +{ + +/** + * This is an implementation of the LogProcessor which creates batches of finished logs and passes + * the export-friendly log data representations to the configured LogExporter. + */ +class BatchLogProcessor : public LogProcessor +{ +public: + /** + * Creates a batch log processor by configuring the specified exporter and other parameters + * as per the official, language-agnostic opentelemetry specs. + * + * @param exporter - The backend exporter to pass the logs to + * @param max_queue_size - The maximum buffer/queue size. After the size is reached, logs are + * dropped. + * @param scheduled_delay_millis - The time interval between two consecutive exports. + * @param max_export_batch_size - The maximum batch size of every export. It must be smaller or + * equal to max_queue_size + */ + explicit BatchLogProcessor( + std::unique_ptr &&exporter, + const size_t max_queue_size = 2048, + const std::chrono::milliseconds scheduled_delay_millis = std::chrono::milliseconds(5000), + const size_t max_export_batch_size = 512); + + /** Makes a new recordable **/ + std::unique_ptr MakeRecordable() noexcept override; + + /** + * Called when the Logger's log method creates a log record + * @param record the log record + */ + + void OnReceive(std::unique_ptr &&record) noexcept override; + + /** + * Export all log records that have not been exported yet. + * + * NOTE: Timeout functionality not supported yet. + */ + bool ForceFlush( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override; + + /** + * Shuts down the processor and does any cleanup required. Completely drains the buffer/queue of + * all its logs and passes them to the exporter. Any subsequent calls to + * ForceFlush or Shutdown will return immediately without doing anything. + * + * NOTE: Timeout functionality not supported yet. + */ + bool Shutdown( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override; + + /** + * Class destructor which invokes the Shutdown() method. + */ + virtual ~BatchLogProcessor() override; + +private: + /** + * The background routine performed by the worker thread. + */ + void DoBackgroundWork(); + + /** + * Exports all logs to the configured exporter. + * + * @param was_force_flush_called - A flag to check if the current export is the result + * of a call to ForceFlush method. If true, then we have to + * notify the main thread to wake it up in the ForceFlush + * method. + */ + void Export(const bool was_for_flush_called); + + /** + * Called when Shutdown() is invoked. Completely drains the queue of all log records and + * passes them to the exporter. + */ + void DrainQueue(); + + /* The configured backend log exporter */ + std::unique_ptr exporter_; + + /* Configurable parameters as per the official *trace* specs */ + const size_t max_queue_size_; + const std::chrono::milliseconds scheduled_delay_millis_; + const size_t max_export_batch_size_; + + /* Synchronization primitives */ + std::condition_variable cv_, force_flush_cv_; + std::mutex cv_m_, force_flush_cv_m_, shutdown_m_; + + /* The buffer/queue to which the ended logs are added */ + common::CircularBuffer buffer_; + + /* Important boolean flags to handle the workflow of the processor */ + std::atomic is_shutdown_{false}; + std::atomic is_force_flush_{false}; + std::atomic is_force_flush_notified_{false}; + + /* The background worker thread */ + std::thread worker_thread_; +}; + +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/exporter.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/exporter.h new file mode 100644 index 000000000..85c58e9f1 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/exporter.h @@ -0,0 +1,62 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_LOGS_PREVIEW + +# include +# include +# include "opentelemetry/nostd/span.h" +# include "opentelemetry/sdk/common/exporter_utils.h" +# include "opentelemetry/sdk/logs/processor.h" +# include "opentelemetry/sdk/logs/recordable.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ +/** + * LogExporter defines the interface that log exporters must implement. + */ +class LogExporter +{ +public: + virtual ~LogExporter() = default; + + /** + * Create a log recordable. This object will be used to record log data and + * will subsequently be passed to LogExporter::Export. Vendors can implement + * custom recordables or use the default LogRecord recordable provided by the + * SDK. + * @return a newly initialized Recordable object + * + * Note: This method must be callable from multiple threads. + */ + virtual std::unique_ptr MakeRecordable() noexcept = 0; + + /** + * Exports the batch of log records to their export destination. + * This method must not be called concurrently for the same exporter instance. + * The exporter may attempt to retry sending the batch, but should drop + * and return kFailure after a certain timeout. + * @param records a span of unique pointers to log records + * @returns an ExportResult code (whether export was success or failure) + */ + virtual sdk::common::ExportResult Export( + const nostd::span> &records) noexcept = 0; + + /** + * Marks the exporter as ShutDown and cleans up any resources as required. + * Shutdown should be called only once for each Exporter instance. + * @param timeout minimum amount of microseconds to wait for shutdown before giving up and + * returning failure. + * @return true if the exporter shutdown succeeded, false otherwise + */ + virtual bool Shutdown( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept = 0; +}; +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/log_record.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/log_record.h new file mode 100644 index 000000000..2a3a78289 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/log_record.h @@ -0,0 +1,193 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_LOGS_PREVIEW + +# include +# include +# include "opentelemetry/sdk/common/attribute_utils.h" +# include "opentelemetry/sdk/logs/recordable.h" +# include "opentelemetry/sdk/resource/resource.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ + +/** + * A default Recordable implemenation to be passed in log statements, + * matching the 10 fields of the Log Data Model. + * (https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#log-and-event-record-definition) + * + */ +class LogRecord final : public Recordable +{ +private: + // Default values are set by the respective data structures' constructors for all fields, + // except the severity field, which must be set manually (an enum with no default value). + opentelemetry::logs::Severity severity_ = opentelemetry::logs::Severity::kInvalid; + const opentelemetry::sdk::resource::Resource *resource_ = nullptr; + common::AttributeMap attributes_map_; + std::string body_; // Currently a simple string, but should be changed to "Any" type + opentelemetry::trace::TraceId trace_id_; + opentelemetry::trace::SpanId span_id_; + opentelemetry::trace::TraceFlags trace_flags_; + opentelemetry::common::SystemTimestamp timestamp_; // uint64 nanoseconds since Unix epoch + +public: + /********** Setters for each field (overrides methods from the Recordable interface) ************/ + + /** + * Set the severity for this log. + * @param severity the severity of the event + */ + void SetSeverity(opentelemetry::logs::Severity severity) noexcept override + { + severity_ = severity; + } + + /** + * Set body field for this log. + * @param message the body to set + */ + void SetBody(nostd::string_view message) noexcept override { body_ = std::string(message); } + + /** + * Set Resource of this log + * @param Resource the resource to set + */ + void SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept override + { + resource_ = &resource; + } + + /** + * Set an attribute of a log. + * @param name the name of the attribute + * @param value the attribute value + */ + + void SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept override + { + attributes_map_.SetAttribute(key, value); + } + + /** + * Set trace id for this log. + * @param trace_id the trace id to set + */ + void SetTraceId(opentelemetry::trace::TraceId trace_id) noexcept override + { + trace_id_ = trace_id; + } + + /** + * Set span id for this log. + * @param span_id the span id to set + */ + virtual void SetSpanId(opentelemetry::trace::SpanId span_id) noexcept override + { + span_id_ = span_id; + } + + /** + * Inject a trace_flags for this log. + * @param trace_flags the span id to set + */ + void SetTraceFlags(opentelemetry::trace::TraceFlags trace_flags) noexcept override + { + trace_flags_ = trace_flags; + } + + /** + * Set the timestamp for this log. + * @param timestamp the timestamp of the event + */ + void SetTimestamp(opentelemetry::common::SystemTimestamp timestamp) noexcept override + { + timestamp_ = timestamp; + } + + /************************** Getters for each field ****************************/ + + /** + * Get the severity for this log + * @return the severity for this log + */ + opentelemetry::logs::Severity GetSeverity() const noexcept { return severity_; } + + /** + * Get the body of this log + * @return the body of this log + */ + std::string GetBody() const noexcept { return body_; } + + /** + * Get the resource for this log + * @return the resource for this log + */ + const opentelemetry::sdk::resource::Resource &GetResource() const noexcept + { + if (nullptr == resource_) + { + return sdk::resource::Resource::GetDefault(); + } + return *resource_; + } + + /** + * Get the attributes for this log + * @return the attributes for this log + */ + const std::unordered_map &GetAttributes() const noexcept + { + return attributes_map_.GetAttributes(); + } + + /** + * Get the trace id for this log + * @return the trace id for this log + */ + opentelemetry::trace::TraceId GetTraceId() const noexcept { return trace_id_; } + + /** + * Get the span id for this log + * @return the span id for this log + */ + opentelemetry::trace::SpanId GetSpanId() const noexcept { return span_id_; } + + /** + * Get the trace flags for this log + * @return the trace flags for this log + */ + opentelemetry::trace::TraceFlags GetTraceFlags() const noexcept { return trace_flags_; } + + /** + * Get the timestamp for this log + * @return the timestamp for this log + */ + opentelemetry::common::SystemTimestamp GetTimestamp() const noexcept { return timestamp_; } + + /** + * Set instrumentation_library for this log. + * @param instrumentation_library the instrumentation library to set + */ + void SetInstrumentationLibrary( + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + &instrumentation_library) noexcept + { + instrumentation_library_ = &instrumentation_library; + } + +private: + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + *instrumentation_library_ = nullptr; +}; +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/logger.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/logger.h new file mode 100644 index 000000000..4eeca2da1 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/logger.h @@ -0,0 +1,77 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/logs/logger.h" +# include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h" +# include "opentelemetry/sdk/logs/logger_context.h" +# include "opentelemetry/sdk/logs/logger_provider.h" + +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ +class LoggerProvider; + +class Logger final : public opentelemetry::logs::Logger +{ +public: + /** + * Initialize a new logger. + * @param name The name of this logger instance + * @param context The logger provider that owns this logger. + */ + explicit Logger( + opentelemetry::nostd::string_view name, + std::shared_ptr context, + std::unique_ptr instrumentation_library = + instrumentationlibrary::InstrumentationLibrary::Create("")) noexcept; + + /** + * Returns the name of this logger. + */ + const opentelemetry::nostd::string_view GetName() noexcept override; + + /** + * Writes a log record into the processor. + * @param severity the severity level of the log event. + * @param message the string message of the log (perhaps support std::fmt or fmt-lib format). + * with the log event. + * @param attributes the attributes, stored as a 2D list of key/value pairs, that are associated + * with the log event. + * @param trace_id the trace id associated with the log event. + * @param span_id the span id associate with the log event. + * @param trace_flags the trace flags associated with the log event. + * @param timestamp the timestamp the log record was created. + * @throws No exceptions under any circumstances. */ + void Log(opentelemetry::logs::Severity severity, + nostd::string_view body, + const opentelemetry::common::KeyValueIterable &attributes, + opentelemetry::trace::TraceId trace_id, + opentelemetry::trace::SpanId span_id, + opentelemetry::trace::TraceFlags trace_flags, + opentelemetry::common::SystemTimestamp timestamp) noexcept override; + + /** Returns the associated instruementation library */ + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary & + GetInstrumentationLibrary() const noexcept; + +private: + // The name of this logger + std::string logger_name_; + + // order of declaration is important here - instrumentation library should destroy after + // logger-context. + std::unique_ptr instrumentation_library_; + std::shared_ptr context_; +}; + +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/logger_context.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/logger_context.h new file mode 100644 index 000000000..01c2f7dd8 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/logger_context.h @@ -0,0 +1,81 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/logs/processor.h" +# include "opentelemetry/sdk/resource/resource.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ +/** + * A class which stores the LoggerContext context. + * + * This class meets the following design criteria: + * - A shared reference between LoggerProvider and Logger instantiated. + * - A thread-safe class that allows updating/altering processor/exporter pipelines + * and sampling config. + * - The owner/destroyer of Processors/Exporters. These will remain active until + * this class is destroyed. I.e. Sampling, Exporting, flushing, Custom Iterator etc. are all ok + * if this object is alive, and they will work together. If this object is destroyed, then no shared + * references to Processor, Exporter, Recordable, Custom Iterator etc. should exist, and all + * associated pipelines will have been flushed. + */ +class LoggerContext +{ +public: + explicit LoggerContext(std::vector> &&processors, + opentelemetry::sdk::resource::Resource resource = + opentelemetry::sdk::resource::Resource::Create({})) noexcept; + + /** + * Attaches a log processor to list of configured processors to this tracer context. + * Processor once attached can't be removed. + * @param processor The new log processor for this tracer. This must not be + * a nullptr. Ownership is given to the `TracerContext`. + * + * Note: This method is not thread safe. + */ + void AddProcessor(std::unique_ptr processor) noexcept; + + /** + * Obtain the configured (composite) processor. + * + * Note: When more than one processor is active, this will + * return an "aggregate" processor + */ + LogProcessor &GetProcessor() const noexcept; + + /** + * Obtain the resource associated with this tracer context. + * @return The resource for this tracer context. + */ + const opentelemetry::sdk::resource::Resource &GetResource() const noexcept; + + /** + * Force all active LogProcessors to flush any buffered logs + * within the given timeout. + */ + bool ForceFlush(std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept; + + /** + * Shutdown the log processor associated with this tracer provider. + */ + bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept; + +private: + // order of declaration is important here - resource object should be destroyed after processor. + opentelemetry::sdk::resource::Resource resource_; + std::unique_ptr processor_; +}; +} // namespace logs +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/logger_provider.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/logger_provider.h new file mode 100755 index 000000000..9afb67ba0 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/logger_provider.h @@ -0,0 +1,131 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0/ + +#pragma once +#ifdef ENABLE_LOGS_PREVIEW + +# include +# include +# include +# include + +# include "opentelemetry/logs/logger_provider.h" +# include "opentelemetry/logs/noop.h" +# include "opentelemetry/nostd/shared_ptr.h" +# include "opentelemetry/sdk/common/atomic_shared_ptr.h" +# include "opentelemetry/sdk/logs/logger.h" +# include "opentelemetry/sdk/logs/logger_context.h" +# include "opentelemetry/sdk/logs/processor.h" + +// Define the maximum number of loggers that are allowed to be registered to the loggerprovider. +// TODO: Add link to logging spec once this is added to it +# define MAX_LOGGER_COUNT 100 + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ +class Logger; + +class LoggerProvider final : public opentelemetry::logs::LoggerProvider +{ +public: + /** + * Initialize a new logger provider + * @param processor The span processor for this logger provider. This must + * not be a nullptr. + * @param resource The resources for this logger provider. + * @param sampler The sampler for this logger provider. This must + * not be a nullptr. + * @param id_generator The custom id generator for this logger provider. This must + * not be a nullptr + */ + explicit LoggerProvider(std::unique_ptr &&processor, + opentelemetry::sdk::resource::Resource resource = + opentelemetry::sdk::resource::Resource::Create({})) noexcept; + + explicit LoggerProvider(std::vector> &&processors, + opentelemetry::sdk::resource::Resource resource = + opentelemetry::sdk::resource::Resource::Create({})) noexcept; + + /** + * Initialize a new logger provider. A processor must later be assigned + * to this logger provider via the AddProcessor() method. + */ + explicit LoggerProvider() noexcept; + + /** + * Initialize a new logger provider with a specified context + * @param context The shared logger configuration/pipeline for this provider. + */ + explicit LoggerProvider(std::shared_ptr context) noexcept; + + ~LoggerProvider(); + + /** + * Creates a logger with the given name, and returns a shared pointer to it. + * If a logger with that name already exists, return a shared pointer to it + * @param logger_name The name of the logger to be created. + * @param options The options for the logger. TODO: Once the logging spec defines it, + * give a list of options that the logger supports. + * @param library_name The version of the library. + * @param library_version The version of the library. + * @param schema_url The schema URL. + */ + nostd::shared_ptr GetLogger( + nostd::string_view logger_name, + nostd::string_view options, + nostd::string_view library_name, + nostd::string_view library_version = "", + nostd::string_view schema_url = "") noexcept override; + /** + * Creates a logger with the given name, and returns a shared pointer to it. + * If a logger with that name already exists, return a shared pointer to it + * @param name The name of the logger to be created. + * @param args The arguments for the logger. TODO: Once the logging spec defines it, + * give a list of arguments that the logger supports. + * @param library_name The version of the library. + * @param library_version The version of the library. + * @param schema_url The schema URL. + */ + nostd::shared_ptr GetLogger( + nostd::string_view logger_name, + nostd::span args, + nostd::string_view library_name, + nostd::string_view library_version = "", + nostd::string_view schema_url = "") noexcept override; + + /** + * Add the processor that is stored internally in the logger provider. + * @param processor The processor to be stored inside the logger provider. + * This must not be a nullptr. + */ + void AddProcessor(std::unique_ptr processor) noexcept; + + /** + * Obtain the resource associated with this logger provider. + * @return The resource for this logger provider. + */ + const opentelemetry::sdk::resource::Resource &GetResource() const noexcept; + + /** + * Shutdown the log processor associated with this log provider. + */ + bool Shutdown() noexcept; + + /** + * Force flush the log processor associated with this log provider. + */ + bool ForceFlush(std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept; + +private: + // order of declaration is important here - loggers should destroy only after context. + std::vector> loggers_; + std::shared_ptr context_; + std::mutex lock_; +}; +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/multi_log_processor.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/multi_log_processor.h new file mode 100644 index 000000000..c1bda24a0 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/multi_log_processor.h @@ -0,0 +1,69 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#ifdef ENABLE_LOGS_PREVIEW + +# include +# include + +# include "opentelemetry/sdk/logs/multi_recordable.h" +# include "opentelemetry/sdk/logs/processor.h" +# include "opentelemetry/sdk/resource/resource.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ + +/** + * Log processor allow hooks for receive method invocations. + * + * Built-in log processors are responsible for batching and conversion of + * logs to exportable representation and passing batches to exporters. + */ +class MultiLogProcessor : public LogProcessor +{ +public: + MultiLogProcessor(std::vector> &&processors); + ~MultiLogProcessor(); + + void AddProcessor(std::unique_ptr &&processor); + + std::unique_ptr MakeRecordable() noexcept override; + + /** + * OnReceive is called by the SDK once a log record has been successfully created. + * @param record the log record + */ + void OnReceive(std::unique_ptr &&record) noexcept override; + + /** + * Exports all log records that have not yet been exported to the configured Exporter. + * @param timeout that the forceflush is required to finish within. + * @return a result code indicating whether it succeeded, failed or timed out + */ + bool ForceFlush( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override; + + /** + * Shuts down the processor and does any cleanup required. + * ShutDown should only be called once for each processor. + * @param timeout minimum amount of microseconds to wait for + * shutdown before giving up and returning failure. + * @return true if the shutdown succeeded, false otherwise + */ + bool Shutdown( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override; + +private: + std::vector> processors_; +}; +} // namespace logs +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/multi_recordable.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/multi_recordable.h new file mode 100644 index 000000000..db3df9622 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/multi_recordable.h @@ -0,0 +1,104 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#ifdef ENABLE_LOGS_PREVIEW + +# include +# include +# include + +# include "opentelemetry/sdk/logs/processor.h" +# include "opentelemetry/sdk/logs/recordable.h" +# include "opentelemetry/sdk/resource/resource.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ +class MultiRecordable final : public Recordable +{ +public: + void AddRecordable(const LogProcessor &processor, + std::unique_ptr recordable) noexcept; + + const std::unique_ptr &GetRecordable(const LogProcessor &processor) const noexcept; + + std::unique_ptr ReleaseRecordable(const LogProcessor &processor) noexcept; + + /** + * Set the timestamp for this log. + * @param timestamp the timestamp to set + */ + void SetTimestamp(opentelemetry::common::SystemTimestamp timestamp) noexcept override; + + /** + * Set the severity for this log. + * @param severity the severity of the event + */ + void SetSeverity(opentelemetry::logs::Severity severity) noexcept override; + + /** + * Set body field for this log. + * @param message the body to set + */ + void SetBody(nostd::string_view message) noexcept override; + + /** + * Set Resource of this log + * @param Resource the resource to set + */ + void SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept override; + + /** + * Set an attribute of a log. + * @param key the name of the attribute + * @param value the attribute value + */ + void SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept override; + + /** + * Set the trace id for this log. + * @param trace_id the trace id to set + */ + void SetTraceId(opentelemetry::trace::TraceId trace_id) noexcept override; + + /** + * Set the span id for this log. + * @param span_id the span id to set + */ + void SetSpanId(opentelemetry::trace::SpanId span_id) noexcept override; + + /** + * Inject trace_flags for this log. + * @param trace_flags the trace flags to set + */ + void SetTraceFlags(opentelemetry::trace::TraceFlags trace_flags) noexcept override; + + /** + * Set instrumentation_library for this log. + * @param instrumentation_library the instrumentation library to set + */ + void SetInstrumentationLibrary( + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + &instrumentation_library) noexcept override; + + /** Returns the associated instruementation library */ + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary & + GetInstrumentationLibrary() const noexcept; + +private: + std::unordered_map> recordables_; + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + *instrumentation_library_ = nullptr; +}; +} // namespace logs +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE + +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/processor.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/processor.h new file mode 100644 index 000000000..36da036f3 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/processor.h @@ -0,0 +1,61 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_LOGS_PREVIEW + +# include +# include +# include "opentelemetry/sdk/logs/recordable.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ +/** + * The Log Processor is responsible for passing log records + * to the configured exporter. + */ +class LogProcessor +{ +public: + virtual ~LogProcessor() = default; + + /** + * Create a log recordable. This requests a new log recordable from the + * associated exporter. + * @return a newly initialized recordable + * + * Note: This method must be callable from multiple threads. + */ + virtual std::unique_ptr MakeRecordable() noexcept = 0; + + /** + * OnReceive is called by the SDK once a log record has been successfully created. + * @param record the log record + */ + virtual void OnReceive(std::unique_ptr &&record) noexcept = 0; + + /** + * Exports all log records that have not yet been exported to the configured Exporter. + * @param timeout that the forceflush is required to finish within. + * @return a result code indicating whether it succeeded, failed or timed out + */ + virtual bool ForceFlush( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept = 0; + + /** + * Shuts down the processor and does any cleanup required. + * ShutDown should only be called once for each processor. + * @param timeout minimum amount of microseconds to wait for + * shutdown before giving up and returning failure. + * @return true if the shutdown succeeded, false otherwise + */ + virtual bool Shutdown( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept = 0; +}; +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/recordable.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/recordable.h new file mode 100644 index 000000000..4a3227373 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/recordable.h @@ -0,0 +1,97 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/common/attribute_value.h" +# include "opentelemetry/common/key_value_iterable.h" +# include "opentelemetry/common/timestamp.h" +# include "opentelemetry/logs/severity.h" +# include "opentelemetry/sdk/common/empty_attributes.h" +# include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h" +# include "opentelemetry/sdk/resource/resource.h" +# include "opentelemetry/trace/span.h" +# include "opentelemetry/trace/span_context.h" +# include "opentelemetry/trace/span_id.h" +# include "opentelemetry/trace/trace_flags.h" +# include "opentelemetry/trace/trace_id.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ +/** + * Maintains a representation of a log in a format that can be processed by a recorder. + * + * This class is thread-compatible. + */ +class Recordable +{ +public: + virtual ~Recordable() = default; + + /** + * Set the timestamp for this log. + * @param timestamp the timestamp to set + */ + virtual void SetTimestamp(opentelemetry::common::SystemTimestamp timestamp) noexcept = 0; + + /** + * Set the severity for this log. + * @param severity the severity of the event + */ + virtual void SetSeverity(opentelemetry::logs::Severity severity) noexcept = 0; + + /** + * Set body field for this log. + * @param message the body to set + */ + virtual void SetBody(nostd::string_view message) noexcept = 0; + + /** + * Set Resource of this log + * @param Resource the resource to set + */ + virtual void SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept = 0; + + /** + * Set an attribute of a log. + * @param key the name of the attribute + * @param value the attribute value + */ + virtual void SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept = 0; + + /** + * Set the trace id for this log. + * @param trace_id the trace id to set + */ + virtual void SetTraceId(opentelemetry::trace::TraceId trace_id) noexcept = 0; + + /** + * Set the span id for this log. + * @param span_id the span id to set + */ + virtual void SetSpanId(opentelemetry::trace::SpanId span_id) noexcept = 0; + + /** + * Inject trace_flags for this log. + * @param trace_flags the trace flags to set + */ + virtual void SetTraceFlags(opentelemetry::trace::TraceFlags trace_flags) noexcept = 0; + + /** + * Set instrumentation_library for this log. + * @param instrumentation_library the instrumentation library to set + */ + virtual void SetInstrumentationLibrary( + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + &instrumentation_library) noexcept = 0; +}; +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/simple_log_processor.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/simple_log_processor.h new file mode 100644 index 000000000..cc3aec47b --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/logs/simple_log_processor.h @@ -0,0 +1,55 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifdef ENABLE_LOGS_PREVIEW + +# include +# include + +# include "opentelemetry/common/spin_lock_mutex.h" +# include "opentelemetry/sdk/logs/exporter.h" +# include "opentelemetry/sdk/logs/processor.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ +/** + * The simple log processor passes all log records + * in a batch of 1 to the configured + * LogExporter. + * + * All calls to the configured LogExporter are synchronized using a + * spin-lock on an atomic_flag. + */ +class SimpleLogProcessor : public LogProcessor +{ + +public: + explicit SimpleLogProcessor(std::unique_ptr &&exporter); + virtual ~SimpleLogProcessor() = default; + + std::unique_ptr MakeRecordable() noexcept override; + + void OnReceive(std::unique_ptr &&record) noexcept override; + + bool ForceFlush( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override; + + bool Shutdown( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override; + +private: + // The configured exporter + std::unique_ptr exporter_; + // The lock used to ensure the exporter is not called concurrently + opentelemetry::common::SpinLockMutex lock_; + // The atomic boolean flag to ensure the ShutDown() function is only called once + std::atomic_flag shutdown_latch_ = ATOMIC_FLAG_INIT; +}; +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/aggregation.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/aggregation.h new file mode 100644 index 000000000..7ec9a6ea2 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/aggregation.h @@ -0,0 +1,55 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/nostd/string_view.h" +# include "opentelemetry/sdk/metrics/data/metric_data.h" +# include "opentelemetry/sdk/metrics/data/point_data.h" +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +class Aggregation +{ +public: + virtual void Aggregate(long value, const PointAttributes &attributes = {}) noexcept = 0; + + virtual void Aggregate(double value, const PointAttributes &attributes = {}) noexcept = 0; + + /** + * Returns the result of the merge of the two aggregations. + * + * This should always assume that the aggregations do not overlap and merge together for a new + * cumulative report. + * + * @param delta the newly captured (delta) aggregation + * @return the result of the merge of the given aggregation. + */ + + virtual std::unique_ptr Merge(const Aggregation &delta) const noexcept = 0; + + /** + * Returns a new delta aggregation by comparing two cumulative measurements. + * + * @param next the newly captured (cumulative) aggregation. + * @return The resulting delta aggregation. + */ + virtual std::unique_ptr Diff(const Aggregation &next) const noexcept = 0; + + /** + * Returns the point data that the aggregation will produce. + * + * @return PointType + */ + + virtual PointType ToPoint() const noexcept = 0; + + virtual ~Aggregation() = default; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/default_aggregation.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/default_aggregation.h new file mode 100644 index 000000000..887e1beb9 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/default_aggregation.h @@ -0,0 +1,146 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/common/spin_lock_mutex.h" +# include "opentelemetry/sdk/metrics/aggregation/aggregation.h" +# include "opentelemetry/sdk/metrics/aggregation/drop_aggregation.h" +# include "opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h" +# include "opentelemetry/sdk/metrics/aggregation/lastvalue_aggregation.h" +# include "opentelemetry/sdk/metrics/aggregation/sum_aggregation.h" +# include "opentelemetry/sdk/metrics/instruments.h" + +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +class DefaultAggregation +{ +public: + static std::unique_ptr CreateAggregation( + const opentelemetry::sdk::metrics::InstrumentDescriptor &instrument_descriptor) + { + switch (instrument_descriptor.type_) + { + case InstrumentType::kCounter: + case InstrumentType::kUpDownCounter: + case InstrumentType::kObservableCounter: + case InstrumentType::kObservableUpDownCounter: + return (instrument_descriptor.value_type_ == InstrumentValueType::kLong) + ? std::move(std::unique_ptr(new LongSumAggregation())) + : std::move(std::unique_ptr(new DoubleSumAggregation())); + break; + case InstrumentType::kHistogram: + return (instrument_descriptor.value_type_ == InstrumentValueType::kLong) + ? std::move(std::unique_ptr(new LongHistogramAggregation())) + : std::move(std::unique_ptr(new DoubleHistogramAggregation())); + break; + case InstrumentType::kObservableGauge: + return (instrument_descriptor.value_type_ == InstrumentValueType::kLong) + ? std::move(std::unique_ptr(new LongLastValueAggregation())) + : std::move(std::unique_ptr(new DoubleLastValueAggregation())); + break; + default: + return std::move(std::unique_ptr(new DropAggregation())); + }; + } + + static std::unique_ptr CreateAggregation(AggregationType aggregation_type, + InstrumentDescriptor instrument_descriptor) + { + switch (aggregation_type) + { + case AggregationType::kDrop: + return std::unique_ptr(new DropAggregation()); + break; + case AggregationType::kHistogram: + if (instrument_descriptor.value_type_ == InstrumentValueType::kLong) + { + return std::unique_ptr(new LongHistogramAggregation()); + } + else + { + return std::unique_ptr(new DoubleHistogramAggregation()); + } + break; + case AggregationType::kLastValue: + if (instrument_descriptor.value_type_ == InstrumentValueType::kLong) + { + return std::unique_ptr(new LongLastValueAggregation()); + } + else + { + return std::unique_ptr(new DoubleLastValueAggregation()); + } + break; + case AggregationType::kSum: + if (instrument_descriptor.value_type_ == InstrumentValueType::kLong) + { + return std::unique_ptr(new LongSumAggregation()); + } + else + { + return std::unique_ptr(new DoubleSumAggregation()); + } + break; + default: + return DefaultAggregation::CreateAggregation(instrument_descriptor); + } + } + + static std::unique_ptr CloneAggregation(AggregationType aggregation_type, + InstrumentDescriptor instrument_descriptor, + const Aggregation &to_copy) + { + const PointType point_data = to_copy.ToPoint(); + switch (aggregation_type) + { + case AggregationType::kDrop: + return std::unique_ptr(new DropAggregation()); + case AggregationType::kHistogram: + if (instrument_descriptor.value_type_ == InstrumentValueType::kLong) + { + return std::unique_ptr( + new LongHistogramAggregation(nostd::get(point_data))); + } + else + { + return std::unique_ptr( + new DoubleHistogramAggregation(nostd::get(point_data))); + } + case AggregationType::kLastValue: + if (instrument_descriptor.value_type_ == InstrumentValueType::kLong) + { + return std::unique_ptr( + new LongLastValueAggregation(nostd::get(point_data))); + } + else + { + return std::unique_ptr( + new DoubleLastValueAggregation(nostd::get(point_data))); + } + case AggregationType::kSum: + if (instrument_descriptor.value_type_ == InstrumentValueType::kLong) + { + return std::unique_ptr( + new LongSumAggregation(nostd::get(point_data))); + } + else + { + return std::unique_ptr( + new DoubleSumAggregation(nostd::get(point_data))); + } + default: + return DefaultAggregation::CreateAggregation(instrument_descriptor); + } + } +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/drop_aggregation.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/drop_aggregation.h new file mode 100644 index 000000000..6c3d89d24 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/drop_aggregation.h @@ -0,0 +1,51 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/common/spin_lock_mutex.h" +# include "opentelemetry/sdk/metrics/aggregation/aggregation.h" + +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +/** + * A null Aggregation which denotes no aggregation should occur. + */ + +class DropAggregation : public Aggregation +{ +public: + DropAggregation() = default; + + DropAggregation(const DropPointData &) {} + + void Aggregate(long value, const PointAttributes &attributes = {}) noexcept override {} + + void Aggregate(double value, const PointAttributes &attributes = {}) noexcept override {} + + std::unique_ptr Merge(const Aggregation &) const noexcept override + { + return std::unique_ptr(new DropAggregation()); + } + + std::unique_ptr Diff(const Aggregation &) const noexcept override + { + return std::unique_ptr(new DropAggregation()); + } + + PointType ToPoint() const noexcept override + { + static DropPointData point_data; + return point_data; + } +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h new file mode 100644 index 000000000..e2a55fba5 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h @@ -0,0 +1,103 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/common/spin_lock_mutex.h" +# include "opentelemetry/sdk/metrics/aggregation/aggregation.h" + +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +class LongHistogramAggregation : public Aggregation +{ +public: + LongHistogramAggregation(); + LongHistogramAggregation(HistogramPointData &&); + LongHistogramAggregation(const HistogramPointData &); + + void Aggregate(long value, const PointAttributes &attributes = {}) noexcept override; + + void Aggregate(double value, const PointAttributes &attributes = {}) noexcept override {} + + /* Returns the result of merge of the existing aggregation with delta aggregation with same + * boundaries */ + std::unique_ptr Merge(const Aggregation &delta) const noexcept override; + + /* Returns the new delta aggregation by comparing existing aggregation with next aggregation with + * same boundaries. Data points for `next` aggregation (sum , bucket-counts) should be more than + * the current aggregation - which is the normal scenario as measurements values are monotonic + * increasing. + */ + std::unique_ptr Diff(const Aggregation &next) const noexcept override; + + PointType ToPoint() const noexcept override; + +private: + opentelemetry::common::SpinLockMutex lock_; + HistogramPointData point_data_; +}; + +class DoubleHistogramAggregation : public Aggregation +{ +public: + DoubleHistogramAggregation(); + DoubleHistogramAggregation(HistogramPointData &&); + DoubleHistogramAggregation(const HistogramPointData &); + + void Aggregate(long value, const PointAttributes &attributes = {}) noexcept override {} + + void Aggregate(double value, const PointAttributes &attributes = {}) noexcept override; + + /* Returns the result of merge of the existing aggregation with delta aggregation with same + * boundaries */ + std::unique_ptr Merge(const Aggregation &delta) const noexcept override; + + /* Returns the new delta aggregation by comparing existing aggregation with next aggregation with + * same boundaries. Data points for `next` aggregation (sum , bucket-counts) should be more than + * the current aggregation - which is the normal scenario as measurements values are monotonic + * increasing. + */ + std::unique_ptr Diff(const Aggregation &next) const noexcept override; + + PointType ToPoint() const noexcept override; + +private: + mutable opentelemetry::common::SpinLockMutex lock_; + mutable HistogramPointData point_data_; +}; + +template +void HistogramMerge(HistogramPointData ¤t, + HistogramPointData &delta, + HistogramPointData &merge) +{ + for (size_t i = 0; i < current.counts_.size(); i++) + { + merge.counts_[i] = current.counts_[i] + delta.counts_[i]; + } + merge.boundaries_ = current.boundaries_; + merge.sum_ = nostd::get(current.sum_) + nostd::get(delta.sum_); + merge.count_ = current.count_ + delta.count_; +} + +template +void HistogramDiff(HistogramPointData ¤t, HistogramPointData &next, HistogramPointData &diff) +{ + for (size_t i = 0; i < current.counts_.size(); i++) + { + diff.counts_[i] = next.counts_[i] - current.counts_[i]; + } + diff.boundaries_ = current.boundaries_; + diff.count_ = next.count_ - current.count_; +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/lastvalue_aggregation.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/lastvalue_aggregation.h new file mode 100644 index 000000000..3b2c08f8c --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/lastvalue_aggregation.h @@ -0,0 +1,63 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/common/spin_lock_mutex.h" +# include "opentelemetry/sdk/metrics/aggregation/aggregation.h" + +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +class LongLastValueAggregation : public Aggregation +{ +public: + LongLastValueAggregation(); + LongLastValueAggregation(LastValuePointData &&); + LongLastValueAggregation(const LastValuePointData &); + + void Aggregate(long value, const PointAttributes &attributes = {}) noexcept override; + + void Aggregate(double value, const PointAttributes &attributes = {}) noexcept override {} + + std::unique_ptr Merge(const Aggregation &delta) const noexcept override; + + std::unique_ptr Diff(const Aggregation &next) const noexcept override; + + PointType ToPoint() const noexcept override; + +private: + opentelemetry::common::SpinLockMutex lock_; + LastValuePointData point_data_; +}; + +class DoubleLastValueAggregation : public Aggregation +{ +public: + DoubleLastValueAggregation(); + DoubleLastValueAggregation(LastValuePointData &&); + DoubleLastValueAggregation(const LastValuePointData &); + + void Aggregate(long value, const PointAttributes &attributes = {}) noexcept override {} + + void Aggregate(double value, const PointAttributes &attributes = {}) noexcept override; + + virtual std::unique_ptr Merge(const Aggregation &delta) const noexcept override; + + virtual std::unique_ptr Diff(const Aggregation &next) const noexcept override; + + PointType ToPoint() const noexcept override; + +private: + mutable opentelemetry::common::SpinLockMutex lock_; + mutable LastValuePointData point_data_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/sum_aggregation.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/sum_aggregation.h new file mode 100644 index 000000000..14f13bd72 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/aggregation/sum_aggregation.h @@ -0,0 +1,64 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/common/spin_lock_mutex.h" +# include "opentelemetry/sdk/metrics/aggregation/aggregation.h" + +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +class LongSumAggregation : public Aggregation +{ +public: + LongSumAggregation(); + LongSumAggregation(SumPointData &&); + LongSumAggregation(const SumPointData &); + + void Aggregate(long value, const PointAttributes &attributes = {}) noexcept override; + + void Aggregate(double value, const PointAttributes &attributes = {}) noexcept override {} + + std::unique_ptr Merge(const Aggregation &delta) const noexcept override; + + std::unique_ptr Diff(const Aggregation &next) const noexcept override; + + PointType ToPoint() const noexcept override; + +private: + opentelemetry::common::SpinLockMutex lock_; + SumPointData point_data_; +}; + +class DoubleSumAggregation : public Aggregation +{ +public: + DoubleSumAggregation(); + DoubleSumAggregation(SumPointData &&); + DoubleSumAggregation(const SumPointData &); + + void Aggregate(long value, const PointAttributes &attributes = {}) noexcept override {} + + void Aggregate(double value, const PointAttributes &attributes = {}) noexcept override; + + std::unique_ptr Merge(const Aggregation &delta) const noexcept override; + + std::unique_ptr Diff(const Aggregation &next) const noexcept override; + + PointType ToPoint() const noexcept override; + +private: + mutable opentelemetry::common::SpinLockMutex lock_; + SumPointData point_data_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/async_instruments.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/async_instruments.h new file mode 100644 index 000000000..8b1f76377 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/async_instruments.h @@ -0,0 +1,115 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/metrics/async_instruments.h" +# include "opentelemetry/metrics/observer_result.h" +# include "opentelemetry/nostd/string_view.h" +# include "opentelemetry/sdk/metrics/instruments.h" +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +template +class Asynchronous +{ +public: + Asynchronous(nostd::string_view name, + void (*callback)(opentelemetry::metrics::ObserverResult &), + nostd::string_view description = "", + nostd::string_view unit = "") + : name_(name), callback_(callback), description_(description), unit_(unit) + {} + +protected: + std::string name_; + void (*callback_)(opentelemetry::metrics::ObserverResult &); + std::string description_; + std::string unit_; +}; + +class LongObservableCounter : public opentelemetry::metrics::ObservableCounter, + public Asynchronous +{ +public: + LongObservableCounter(nostd::string_view name, + void (*callback)(opentelemetry::metrics::ObserverResult &), + nostd::string_view description = "", + nostd::string_view unit = "") + : Asynchronous(name, callback, description, unit) + + {} +}; + +class DoubleObservableCounter : public opentelemetry::metrics::ObservableCounter, + public Asynchronous +{ +public: + DoubleObservableCounter(nostd::string_view name, + void (*callback)(opentelemetry::metrics::ObserverResult &), + nostd::string_view description = "", + nostd::string_view unit = "") + : Asynchronous(name, callback, description, unit) + + {} +}; + +class LongObservableGauge : public opentelemetry::metrics::ObservableGauge, + public Asynchronous +{ +public: + LongObservableGauge(nostd::string_view name, + void (*callback)(opentelemetry::metrics::ObserverResult &), + nostd::string_view description = "", + nostd::string_view unit = "") + : Asynchronous(name, callback, description, unit) + + {} +}; + +class DoubleObservableGauge : public opentelemetry::metrics::ObservableGauge, + public Asynchronous +{ +public: + DoubleObservableGauge(nostd::string_view name, + void (*callback)(opentelemetry::metrics::ObserverResult &), + nostd::string_view description = "", + nostd::string_view unit = "") + : Asynchronous(name, callback, description, unit) + + {} +}; + +class LongObservableUpDownCounter : public opentelemetry::metrics::ObservableUpDownCounter, + public Asynchronous +{ +public: + LongObservableUpDownCounter(nostd::string_view name, + void (*callback)(opentelemetry::metrics::ObserverResult &), + nostd::string_view description = "", + nostd::string_view unit = "") + : Asynchronous(name, callback, description, unit) + + {} +}; + +class DoubleObservableUpDownCounter + : public opentelemetry::metrics::ObservableUpDownCounter, + public Asynchronous +{ +public: + DoubleObservableUpDownCounter(nostd::string_view name, + void (*callback)(opentelemetry::metrics::ObserverResult &), + nostd::string_view description = "", + nostd::string_view unit = "") + : Asynchronous(name, callback, description, unit) + {} +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/data/metric_data.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/data/metric_data.h new file mode 100644 index 000000000..738d4540f --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/data/metric_data.h @@ -0,0 +1,42 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/nostd/variant.h" +# include "opentelemetry/sdk/common/attribute_utils.h" +# include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h" +# include "opentelemetry/sdk/metrics/data/point_data.h" +# include "opentelemetry/sdk/metrics/instruments.h" +# include "opentelemetry/sdk/resource/resource.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +using PointAttributes = opentelemetry::sdk::common::OrderedAttributeMap; +using PointType = opentelemetry::nostd:: + variant; + +struct PointDataAttributes +{ + PointAttributes attributes; + PointType point_data; +}; + +class MetricData +{ +public: + InstrumentDescriptor instrument_descriptor; + opentelemetry::common::SystemTimestamp start_ts; + opentelemetry::common::SystemTimestamp end_ts; + std::vector point_data_attr_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/data/point_data.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/data/point_data.h new file mode 100644 index 000000000..714f96c9a --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/data/point_data.h @@ -0,0 +1,78 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/common/timestamp.h" +# include "opentelemetry/nostd/variant.h" +# include "opentelemetry/sdk/metrics/instruments.h" +# include "opentelemetry/version.h" + +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +using ValueType = nostd::variant; +using ListType = nostd::variant, std::list>; + +// TODO: remove ctors and initializers from below classes when GCC<5 stops shipping on Ubuntu + +class SumPointData +{ +public: + // TODO: remove ctors and initializers when GCC<5 stops shipping on Ubuntu + SumPointData(SumPointData &&) = default; + SumPointData(const SumPointData &) = default; + SumPointData &operator=(SumPointData &&) = default; + SumPointData() = default; + + ValueType value_ = {}; +}; + +class LastValuePointData +{ +public: + // TODO: remove ctors and initializers when GCC<5 stops shipping on Ubuntu + LastValuePointData(LastValuePointData &&) = default; + LastValuePointData(const LastValuePointData &) = default; + LastValuePointData &operator=(LastValuePointData &&) = default; + LastValuePointData() = default; + + ValueType value_ = {}; + bool is_lastvalue_valid_ = {}; + opentelemetry::common::SystemTimestamp sample_ts_ = {}; +}; + +class HistogramPointData +{ +public: + // TODO: remove ctors and initializers when GCC<5 stops shipping on Ubuntu + HistogramPointData(HistogramPointData &&) = default; + HistogramPointData &operator=(HistogramPointData &&) = default; + HistogramPointData(const HistogramPointData &) = default; + HistogramPointData() = default; + + ListType boundaries_ = {}; + ValueType sum_ = {}; + std::vector counts_ = {}; + uint64_t count_ = {}; +}; + +class DropPointData +{ +public: + // TODO: remove ctors and initializers when GCC<5 stops shipping on Ubuntu + DropPointData(DropPointData &&) = default; + DropPointData(const DropPointData &) = default; + DropPointData() = default; + DropPointData &operator=(DropPointData &&) = default; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/always_sample_filter.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/always_sample_filter.h new file mode 100644 index 000000000..5e7f0436e --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/always_sample_filter.h @@ -0,0 +1,43 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/exemplar/filter.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +class AlwaysSampleFilter final : public ExemplarFilter +{ +public: + static nostd::shared_ptr GetAlwaysSampleFilter() + { + static nostd::shared_ptr alwaysSampleFilter{new AlwaysSampleFilter{}}; + return alwaysSampleFilter; + } + + bool ShouldSampleMeasurement(long value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context) noexcept override + { + return true; + } + + bool ShouldSampleMeasurement(double value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context) noexcept override + { + return true; + } + +private: + explicit AlwaysSampleFilter() = default; +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/data.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/data.h new file mode 100644 index 000000000..14eac6249 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/data.h @@ -0,0 +1,45 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/common/timestamp.h" +# include "opentelemetry/context/context.h" +# include "opentelemetry/sdk/common/attribute_utils.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +using MetricAttributes = opentelemetry::sdk::common::OrderedAttributeMap; +/** + * A sample input measurement. + * + * Exemplars also hold information about the environment when the measurement was recorded, for + * example the span and trace ID of the active span when the exemplar was recorded. + */ +class ExemplarData +{ +public: + /** + * The set of key/value pairs that were filtered out by the aggregator, but recorded alongside the + * original measurement. Only key/value pairs that were filtered out by the aggregator should be + * included + */ + MetricAttributes GetFilteredAttributes(); + + /** Returns the timestamp in nanos when measurement was collected. */ + opentelemetry::common::SystemTimestamp GetEpochNanos(); + + /** + * Returns the SpanContext associated with this exemplar. If the exemplar was not recorded + * inside a sampled trace, the Context will be invalid. + */ + opentelemetry::context::Context GetSpanContext(); +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/filter.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/filter.h new file mode 100644 index 000000000..4b512e131 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/filter.h @@ -0,0 +1,39 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/context/context.h" +# include "opentelemetry/sdk/common/attribute_utils.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +using MetricAttributes = opentelemetry::sdk::common::OrderedAttributeMap; + +/** + * Exemplar filters are used to pre-filter measurements before attempting to store them in a + * reservoir. + */ +class ExemplarFilter +{ +public: + // Returns whether or not a reservoir should attempt to filter a measurement. + virtual bool ShouldSampleMeasurement(long value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context) noexcept = 0; + + // Returns whether or not a reservoir should attempt to filter a measurement. + virtual bool ShouldSampleMeasurement(double value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context) noexcept = 0; + + virtual ~ExemplarFilter() = default; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/never_sample_filter.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/never_sample_filter.h new file mode 100644 index 000000000..38f51778c --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/never_sample_filter.h @@ -0,0 +1,43 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/exemplar/filter.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +class NeverSampleFilter final : public ExemplarFilter +{ +public: + static nostd::shared_ptr GetNeverSampleFilter() + { + nostd::shared_ptr neverSampleFilter{new NeverSampleFilter{}}; + return neverSampleFilter; + } + + bool ShouldSampleMeasurement(long value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context) noexcept override + { + return false; + } + + bool ShouldSampleMeasurement(double value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context) noexcept override + { + return false; + } + +private: + explicit NeverSampleFilter() = default; +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h new file mode 100644 index 000000000..1fe0586d2 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h @@ -0,0 +1,55 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include +# include "opentelemetry/context/context.h" +# include "opentelemetry/nostd/shared_ptr.h" +# include "opentelemetry/sdk/common/attribute_utils.h" +# include "opentelemetry/sdk/metrics/exemplar/reservoir.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +class NoExemplarReservoir final : public ExemplarReservoir +{ + +public: + static nostd::shared_ptr GetNoExemplarReservoir() + { + return nostd::shared_ptr{new NoExemplarReservoir{}}; + } + + void OfferMeasurement(long value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context, + const opentelemetry::common::SystemTimestamp ×tamp) noexcept override + { + // Stores nothing + } + + void OfferMeasurement(double value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context, + const opentelemetry::common::SystemTimestamp ×tamp) noexcept override + { + // Stores nothing. + } + + std::vector CollectAndReset( + const MetricAttributes &pointAttributes) noexcept override + { + return std::vector{}; + } + +private: + explicit NoExemplarReservoir() = default; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir.h new file mode 100644 index 000000000..25e8421d6 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/exemplar/reservoir.h @@ -0,0 +1,55 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include +# include "opentelemetry/sdk/metrics/exemplar/data.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +/** + * An interface for an exemplar reservoir of samples. + * + *

This represents a reservoir for a specific "point" of metric data. + */ +class ExemplarReservoir +{ +public: + virtual ~ExemplarReservoir() = default; + + /** Offers a long measurement to be sampled. */ + virtual void OfferMeasurement( + long value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context, + const opentelemetry::common::SystemTimestamp ×tamp) noexcept = 0; + + /** Offers a double measurement to be sampled. */ + virtual void OfferMeasurement( + double value, + const MetricAttributes &attributes, + const opentelemetry::context::Context &context, + const opentelemetry::common::SystemTimestamp ×tamp) noexcept = 0; + + /** + * Builds vector of Exemplars for exporting from the current reservoir. + * + *

Additionally, clears the reservoir for the next sampling period. + * + * @param pointAttributes the Attributes associated with the metric point. + * ExemplarDatas should filter these out of their final data state. + * @return A vector of sampled exemplars for this point. Implementers are expected to + * filter out pointAttributes from the original recorded attributes. + */ + virtual std::vector CollectAndReset( + const MetricAttributes &pointAttributes) noexcept = 0; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/export/metric_producer.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/export/metric_producer.h new file mode 100644 index 000000000..d3b38759a --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/export/metric_producer.h @@ -0,0 +1,57 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h" +# include "opentelemetry/sdk/metrics/data/metric_data.h" +# include "opentelemetry/sdk/resource/resource.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +/** + * Metric Data to be exported along with resources and + * Instrumentation library. + */ +struct InstrumentationInfoMetrics +{ + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + *instrumentation_library_; + std::vector metric_data_; +}; + +struct ResourceMetrics +{ + const opentelemetry::sdk::resource::Resource *resource_; + std::vector instrumentation_info_metric_data_; +}; + +/** + * MetricProducer is the interface that is used to make metric data available to the + * OpenTelemetry exporters. Implementations should be stateful, in that each call to + * `Collect` will return any metric generated since the last call was made. + * + *

Implementations must be thread-safe. + */ + +class MetricProducer +{ +public: + /** + * The callback to be called for each metric exporter. This will only be those + * metrics that have been produced since the last time this method was called. + * + * @return a status of completion of method. + */ + virtual bool Collect( + nostd::function_ref callback) noexcept = 0; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.h new file mode 100644 index 000000000..29125a6ea --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.h @@ -0,0 +1,72 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW + +# include "opentelemetry/sdk/metrics/metric_reader.h" +# include "opentelemetry/version.h" + +# include +# include +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +class MetricExporter; +/** + * Struct to hold PeriodicExortingMetricReader options. + */ + +constexpr std::chrono::milliseconds kExportIntervalMillis = std::chrono::milliseconds(60000); +constexpr std::chrono::milliseconds kExportTimeOutMillis = std::chrono::milliseconds(30000); +struct PeriodicExportingMetricReaderOptions +{ + + /* The time interval between two consecutive exports. */ + std::chrono::milliseconds export_interval_millis = + std::chrono::milliseconds(kExportIntervalMillis); + + /* how long the export can run before it is cancelled. */ + std::chrono::milliseconds export_timeout_millis = std::chrono::milliseconds(kExportTimeOutMillis); +}; + +class PeriodicExportingMetricReader : public MetricReader +{ + +public: + PeriodicExportingMetricReader( + std::unique_ptr exporter, + const PeriodicExportingMetricReaderOptions &option, + AggregationTemporality aggregation_temporality = AggregationTemporality::kCumulative); + +private: + bool OnForceFlush(std::chrono::microseconds timeout) noexcept override; + + bool OnShutDown(std::chrono::microseconds timeout) noexcept override; + + void OnInitialized() noexcept override; + + std::unique_ptr exporter_; + std::chrono::milliseconds export_interval_millis_; + std::chrono::milliseconds export_timeout_millis_; + + void DoBackgroundWork(); + + /* The background worker thread */ + std::thread worker_thread_; + + /* Synchronization primitives */ + std::condition_variable cv_; + std::mutex cv_m_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/instruments.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/instruments.h new file mode 100644 index 000000000..5d8535714 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/instruments.h @@ -0,0 +1,70 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/common/attribute_utils.h" +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +enum class InstrumentType +{ + kCounter, + kHistogram, + kUpDownCounter, + kObservableCounter, + kObservableGauge, + kObservableUpDownCounter +}; + +enum class InstrumentValueType +{ + kInt, + kLong, + kFloat, + kDouble +}; + +enum class AggregationType +{ + kDrop, + kHistogram, + kLastValue, + kSum, + kDefault +}; + +enum class AggregationTemporality +{ + kUnspecified, + kDelta, + kCumulative +}; + +struct InstrumentDescriptor +{ + std::string name_; + std::string description_; + std::string unit_; + InstrumentType type_; + InstrumentValueType value_type_; +}; + +using MetricAttributes = opentelemetry::sdk::common::OrderedAttributeMap; + +/*class InstrumentSelector { +public: +InstrumentSelector(opentelemetry::nostd::string_view name, +opentelemetry::sdk::metrics::InstrumentType type): name_(name.data()), type_(type) {} InstrumentType +GetType(){return type_;} std::string GetNameFilter() { return name_;} + +private: +std::string name_; +InstrumentType type_; +};*/ +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/meter.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/meter.h new file mode 100644 index 000000000..44e54adf1 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/meter.h @@ -0,0 +1,156 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include +# include "opentelemetry/metrics/meter.h" +# include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h" +# include "opentelemetry/sdk/metrics/instruments.h" +# include "opentelemetry/sdk/metrics/meter_context.h" +# include "opentelemetry/sdk/metrics/state/async_metric_storage.h" + +# include "opentelemetry/sdk/resource/resource.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +class MetricStorage; +class WritableMetricStorage; + +class Meter final : public opentelemetry::metrics::Meter +{ +public: + /** Construct a new Meter with the given pipeline. */ + explicit Meter(std::shared_ptr meter_context, + std::unique_ptr + instrumentation_library = + opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary::Create( + "")) noexcept; + + nostd::shared_ptr> CreateLongCounter( + nostd::string_view name, + nostd::string_view description = "", + nostd::string_view unit = "") noexcept override; + + nostd::shared_ptr> CreateDoubleCounter( + nostd::string_view name, + nostd::string_view description = "", + nostd::string_view unit = "") noexcept override; + + void CreateLongObservableCounter(nostd::string_view name, + void (*callback)(opentelemetry::metrics::ObserverResult &, + void *), + nostd::string_view description = "", + nostd::string_view unit = "", + void *state = nullptr) noexcept override; + + void CreateDoubleObservableCounter( + nostd::string_view name, + void (*callback)(opentelemetry::metrics::ObserverResult &, void *), + nostd::string_view description = "", + nostd::string_view unit = "", + void *state = nullptr) noexcept override; + + nostd::shared_ptr> CreateLongHistogram( + nostd::string_view name, + nostd::string_view description = "", + nostd::string_view unit = "") noexcept override; + + nostd::shared_ptr> CreateDoubleHistogram( + nostd::string_view name, + nostd::string_view description = "", + nostd::string_view unit = "") noexcept override; + + void CreateLongObservableGauge(nostd::string_view name, + void (*callback)(opentelemetry::metrics::ObserverResult &, + void *), + nostd::string_view description = "", + nostd::string_view unit = "", + void *state = nullptr) noexcept override; + + void CreateDoubleObservableGauge( + nostd::string_view name, + void (*callback)(opentelemetry::metrics::ObserverResult &, void *), + nostd::string_view description = "", + nostd::string_view unit = "", + void *state = nullptr) noexcept override; + + nostd::shared_ptr> CreateLongUpDownCounter( + nostd::string_view name, + nostd::string_view description = "", + nostd::string_view unit = "") noexcept override; + + nostd::shared_ptr> CreateDoubleUpDownCounter( + nostd::string_view name, + nostd::string_view description = "", + nostd::string_view unit = "") noexcept override; + + void CreateLongObservableUpDownCounter( + nostd::string_view name, + void (*callback)(opentelemetry::metrics::ObserverResult &, void *), + nostd::string_view description = "", + nostd::string_view unit = "", + void *state = nullptr) noexcept override; + + void CreateDoubleObservableUpDownCounter( + nostd::string_view name, + void (*callback)(opentelemetry::metrics::ObserverResult &, void *), + nostd::string_view description = "", + nostd::string_view unit = "", + void *state = nullptr) noexcept override; + + /** Returns the associated instruementation library */ + const sdk::instrumentationlibrary::InstrumentationLibrary *GetInstrumentationLibrary() + const noexcept; + + /** collect metrics across all the instruments configured for the meter **/ + std::vector Collect(CollectorHandle *collector, + opentelemetry::common::SystemTimestamp collect_ts) noexcept; + +private: + // order of declaration is important here - instrumentation library should destroy after + // meter-context. + std::unique_ptr instrumentation_library_; + std::shared_ptr meter_context_; + // Mapping between instrument-name and Aggregation Storage. + std::unordered_map> storage_registry_; + + std::unique_ptr RegisterMetricStorage( + InstrumentDescriptor &instrument_descriptor); + + template + void RegisterAsyncMetricStorage(InstrumentDescriptor &instrument_descriptor, + void (*callback)(opentelemetry::metrics::ObserverResult &, + void *), + void *state = nullptr) + { + auto view_registry = meter_context_->GetViewRegistry(); + auto success = view_registry->FindViews( + instrument_descriptor, *instrumentation_library_, + [this, &instrument_descriptor, callback, state](const View &view) { + auto view_instr_desc = instrument_descriptor; + if (!view.GetName().empty()) + { + view_instr_desc.name_ = view.GetName(); + } + if (!view.GetDescription().empty()) + { + view_instr_desc.description_ = view.GetDescription(); + } + auto storage = std::shared_ptr>( + new AsyncMetricStorage(view_instr_desc, view.GetAggregationType(), callback, + &view.GetAttributesProcessor(), state)); + storage_registry_[instrument_descriptor.name_] = storage; + return true; + }); + } +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/meter_context.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/meter_context.h new file mode 100644 index 000000000..e35700175 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/meter_context.h @@ -0,0 +1,133 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW + +# include "opentelemetry/common/spin_lock_mutex.h" +# include "opentelemetry/sdk/metrics/state/metric_collector.h" +# include "opentelemetry/sdk/metrics/view/instrument_selector.h" +# include "opentelemetry/sdk/metrics/view/meter_selector.h" +# include "opentelemetry/sdk/metrics/view/view_registry.h" +# include "opentelemetry/sdk/resource/resource.h" +# include "opentelemetry/version.h" + +# include +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +// forward declaration +class Meter; +class MetricReader; + +/** + * A class which stores the MeterProvider context. + + */ +class MeterContext : public std::enable_shared_from_this +{ +public: + /** + * Initialize a new meter provider + * @param readers The readers to be configured with meter context. + * @param views The views to be configured with meter context. + * @param resource The resource for this meter context. + */ + MeterContext( + std::unique_ptr views = std::unique_ptr(new ViewRegistry()), + opentelemetry::sdk::resource::Resource resource = + opentelemetry::sdk::resource::Resource::Create({})) noexcept; + + /** + * Obtain the resource associated with this meter context. + * @return The resource for this meter context + */ + const opentelemetry::sdk::resource::Resource &GetResource() const noexcept; + + /** + * Obtain the View Registry configured + * @return The reference to view registry + */ + ViewRegistry *GetViewRegistry() const noexcept; + + /** + * Obtain the configured meters. + * + */ + nostd::span> GetMeters() noexcept; + + /** + * Obtain the configured collectors. + * + */ + nostd::span> GetCollectors() noexcept; + + /** + * GET SDK Start time + * + */ + opentelemetry::common::SystemTimestamp GetSDKStartTime() noexcept; + + /** + * Attaches a metric reader to list of configured readers for this Meter context. + * @param reader The metric reader for this meter context. This + * must not be a nullptr. + * + * Note: This reader may not receive any in-flight meter data, but will get newly created meter + * data. Note: This method is not thread safe, and should ideally be called from main thread. + */ + void AddMetricReader(std::unique_ptr reader) noexcept; + + /** + * Attaches a View to list of configured Views for this Meter context. + * @param view The Views for this meter context. This + * must not be a nullptr. + * + * Note: This view may not receive any in-flight meter data, but will get newly created meter + * data. Note: This method is not thread safe, and should ideally be called from main thread. + */ + void AddView(std::unique_ptr instrument_selector, + std::unique_ptr meter_selector, + std::unique_ptr view) noexcept; + + /** + * Adds a meter to the list of configured meters. + * Note: This method is INTERNAL to sdk not thread safe. + * + * @param meter + */ + void AddMeter(std::shared_ptr meter); + + /** + * Force all active Collectors to flush any buffered meter data + * within the given timeout. + */ + + bool ForceFlush(std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept; + + /** + * Shutdown the Collectors associated with this meter provider. + */ + bool Shutdown() noexcept; + +private: + opentelemetry::sdk::resource::Resource resource_; + std::vector> collectors_; + std::unique_ptr views_; + opentelemetry::common::SystemTimestamp sdk_start_ts_; + std::vector> meters_; + + std::atomic_flag shutdown_latch_ = ATOMIC_FLAG_INIT; + opentelemetry::common::SpinLockMutex forceflush_lock_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/meter_provider.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/meter_provider.h new file mode 100644 index 000000000..685f43e74 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/meter_provider.h @@ -0,0 +1,95 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include +# include +# include +# include "opentelemetry/metrics/meter.h" +# include "opentelemetry/metrics/meter_provider.h" +# include "opentelemetry/nostd/shared_ptr.h" +# include "opentelemetry/sdk/metrics/meter.h" +# include "opentelemetry/sdk/metrics/meter_context.h" +# include "opentelemetry/sdk/resource/resource.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +// forward declaration +class MetricCollector; +class MetricReader; + +class MeterProvider final : public opentelemetry::metrics::MeterProvider +{ +public: + /** + * Initialize a new meter provider + * @param views The views for this meter provider + * @param resource The resources for this meter provider. + */ + MeterProvider( + std::unique_ptr views = std::unique_ptr(new ViewRegistry()), + sdk::resource::Resource resource = sdk::resource::Resource::Create({})) noexcept; + + /** + * Initialize a new meter provider with a specified context + * @param context The shared meter configuration/pipeline for this provider. + */ + explicit MeterProvider(std::shared_ptr context) noexcept; + + nostd::shared_ptr GetMeter( + nostd::string_view name, + nostd::string_view version = "", + nostd::string_view schema_url = "") noexcept override; + + /** + * Obtain the resource associated with this meter provider. + * @return The resource for this meter provider. + */ + const sdk::resource::Resource &GetResource() const noexcept; + + /** + * Attaches a metric reader to list of configured readers for this Meter providers. + * @param reader The metric reader for this meter provider. This + * must not be a nullptr. + * + * Note: This reader may not receive any in-flight meter data, but will get newly created meter + * data. Note: This method is not thread safe, and should ideally be called from main thread. + */ + void AddMetricReader(std::unique_ptr reader) noexcept; + + /** + * Attaches a View to list of configured Views for this Meter provider. + * @param view The Views for this meter provider. This + * must not be a nullptr. + * + * Note: This view may not receive any in-flight meter data, but will get newly created meter + * data. Note: This method is not thread safe, and should ideally be called from main thread. + */ + void AddView(std::unique_ptr instrument_selector, + std::unique_ptr meter_selector, + std::unique_ptr view) noexcept; + + /** + * Shutdown the meter provider. + */ + bool Shutdown() noexcept; + + /** + * Force flush the meter provider. + */ + bool ForceFlush(std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept; + +private: + std::shared_ptr context_; + std::mutex lock_; +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/metric_exporter.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/metric_exporter.h new file mode 100644 index 000000000..3217b83df --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/metric_exporter.h @@ -0,0 +1,55 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW + +# include "opentelemetry/nostd/span.h" +# include "opentelemetry/sdk/common/exporter_utils.h" +# include "opentelemetry/sdk/metrics/export/metric_producer.h" +# include "opentelemetry/version.h" + +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +class MetricData; +/** + * MetricExporter defines the interface to be used by metrics libraries to + * push metrics data to the OpenTelemetry exporters. + */ +class MetricExporter +{ +public: + virtual ~MetricExporter() = default; + + /** + * Exports a batch of metrics data. This method must not be called + * concurrently for the same exporter instance. + * @param data metrics data + */ + virtual opentelemetry::sdk::common::ExportResult Export(const ResourceMetrics &data) noexcept = 0; + + /** + * Force flush the exporter. + */ + virtual bool ForceFlush( + std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept = 0; + + /** + * Shut down the metric exporter. + * @param timeout an optional timeout. + * @return return the status of the operation. + */ + virtual bool Shutdown( + std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept = 0; +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/metric_reader.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/metric_reader.h new file mode 100644 index 000000000..94924315f --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/metric_reader.h @@ -0,0 +1,72 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/common/spin_lock_mutex.h" +# include "opentelemetry/sdk/common/global_log_handler.h" +# include "opentelemetry/sdk/metrics/data/metric_data.h" +# include "opentelemetry/sdk/metrics/export/metric_producer.h" +# include "opentelemetry/sdk/metrics/instruments.h" +# include "opentelemetry/version.h" + +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +/** + * MetricReader defines the interface to collect metrics from SDK + */ +class MetricReader +{ +public: + MetricReader( + AggregationTemporality aggregation_temporality = AggregationTemporality::kCumulative); + + void SetMetricProducer(MetricProducer *metric_producer); + + /** + * Collect the metrics from SDK. + * @return return the status of the operation. + */ + bool Collect(nostd::function_ref callback) noexcept; + + AggregationTemporality GetAggregationTemporality() const noexcept; + + /** + * Shutdown the meter reader. + */ + bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept; + + /** + * Force flush the metric read by the reader. + */ + bool ForceFlush(std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept; + + virtual ~MetricReader() = default; + +private: + virtual bool OnForceFlush(std::chrono::microseconds timeout) noexcept = 0; + + virtual bool OnShutDown(std::chrono::microseconds timeout) noexcept = 0; + + virtual void OnInitialized() noexcept {} + +protected: + bool IsShutdown() const noexcept; + +private: + MetricProducer *metric_producer_; + AggregationTemporality aggregation_temporality_; + mutable opentelemetry::common::SpinLockMutex lock_; + bool shutdown_; +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/observer_result.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/observer_result.h new file mode 100644 index 000000000..ca7227bc5 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/observer_result.h @@ -0,0 +1,48 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/common/key_value_iterable.h" +# include "opentelemetry/metrics/observer_result.h" +# include "opentelemetry/sdk/metrics/state/attributes_hashmap.h" +# include "opentelemetry/sdk/metrics/view/attributes_processor.h" + +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +template +class ObserverResult final : public opentelemetry::metrics::ObserverResult +{ +public: + ObserverResult(const AttributesProcessor *attributes_processor) + : attributes_processor_(attributes_processor) + {} + + void Observe(T value) noexcept override { data_.insert({{}, value}); } + + void Observe(T value, const opentelemetry::common::KeyValueIterable &attributes) noexcept override + { + auto attr = attributes_processor_->process(attributes); + data_.insert({attr, value}); + } + + const std::unordered_map &GetMeasurements() + { + return data_; + } + +private: + std::unordered_map data_; + + const AttributesProcessor *attributes_processor_; +}; +} // namespace metrics +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/async_metric_storage.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/async_metric_storage.h new file mode 100644 index 000000000..e5dcbc273 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/async_metric_storage.h @@ -0,0 +1,95 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/common/attributemap_hash.h" +# include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h" +# include "opentelemetry/sdk/metrics/instruments.h" +# include "opentelemetry/sdk/metrics/observer_result.h" +# include "opentelemetry/sdk/metrics/state/attributes_hashmap.h" +# include "opentelemetry/sdk/metrics/state/metric_collector.h" +# include "opentelemetry/sdk/metrics/state/metric_storage.h" +# include "opentelemetry/sdk/metrics/state/temporal_metric_storage.h" +# include "opentelemetry/sdk/metrics/view/attributes_processor.h" + +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +template +class AsyncMetricStorage : public MetricStorage +{ +public: + AsyncMetricStorage(InstrumentDescriptor instrument_descriptor, + const AggregationType aggregation_type, + void (*measurement_callback)(opentelemetry::metrics::ObserverResult &, + void *), + const AttributesProcessor *attributes_processor, + void *state = nullptr) + : instrument_descriptor_(instrument_descriptor), + aggregation_type_{aggregation_type}, + measurement_collection_callback_{measurement_callback}, + attributes_processor_{attributes_processor}, + state_{state}, + cumulative_hash_map_(new AttributesHashMap()), + temporal_metric_storage_(instrument_descriptor) + {} + + bool Collect(CollectorHandle *collector, + nostd::span> collectors, + opentelemetry::common::SystemTimestamp sdk_start_ts, + opentelemetry::common::SystemTimestamp collection_ts, + nostd::function_ref metric_collection_callback) noexcept override + { + opentelemetry::sdk::metrics::ObserverResult ob_res(attributes_processor_); + + // read the measurement using configured callback + measurement_collection_callback_(ob_res, state_); + std::shared_ptr delta_hash_map(new AttributesHashMap()); + // process the read measurements - aggregate and store in hashmap + for (auto &measurement : ob_res.GetMeasurements()) + { + auto aggr = DefaultAggregation::CreateAggregation(aggregation_type_, instrument_descriptor_); + aggr->Aggregate(measurement.second); + auto prev = cumulative_hash_map_->Get(measurement.first); + if (prev) + { + auto delta = prev->Diff(*aggr); + cumulative_hash_map_->Set(measurement.first, + DefaultAggregation::CloneAggregation( + aggregation_type_, instrument_descriptor_, *delta)); + delta_hash_map->Set(measurement.first, std::move(delta)); + } + else + { + cumulative_hash_map_->Set( + measurement.first, + DefaultAggregation::CloneAggregation(aggregation_type_, instrument_descriptor_, *aggr)); + delta_hash_map->Set(measurement.first, std::move(aggr)); + } + } + + return temporal_metric_storage_.buildMetrics(collector, collectors, sdk_start_ts, collection_ts, + std::move(delta_hash_map), + metric_collection_callback); + } + +private: + InstrumentDescriptor instrument_descriptor_; + AggregationType aggregation_type_; + void (*measurement_collection_callback_)(opentelemetry::metrics::ObserverResult &, void *); + const AttributesProcessor *attributes_processor_; + void *state_; + std::unique_ptr cumulative_hash_map_; + TemporalMetricStorage temporal_metric_storage_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/attributes_hashmap.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/attributes_hashmap.h new file mode 100644 index 000000000..50d40e0ae --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/attributes_hashmap.h @@ -0,0 +1,124 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/common/spin_lock_mutex.h" +# include "opentelemetry/nostd/function_ref.h" +# include "opentelemetry/sdk/common/attribute_utils.h" +# include "opentelemetry/sdk/common/attributemap_hash.h" +# include "opentelemetry/sdk/metrics/aggregation/aggregation.h" +# include "opentelemetry/sdk/metrics/instruments.h" +# include "opentelemetry/version.h" + +# include +# include +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +using opentelemetry::sdk::common::OrderedAttributeMap; + +class AttributeHashGenerator +{ +public: + size_t operator()(const MetricAttributes &attributes) const + { + return opentelemetry::sdk::common::GetHashForAttributeMap(attributes); + } +}; + +class AttributesHashMap +{ +public: + Aggregation *Get(const MetricAttributes &attributes) const + { + std::lock_guard guard(lock_); + auto it = hash_map_.find(attributes); + if (it != hash_map_.end()) + { + return it->second.get(); + } + return nullptr; + } + + /** + * @return check if key is present in hash + * + */ + bool Has(const MetricAttributes &attributes) const + { + std::lock_guard guard(lock_); + return (hash_map_.find(attributes) == hash_map_.end()) ? false : true; + } + + /** + * @return the pointer to value for given key if present. + * If not present, it uses the provided callback to generate + * value and store in the hash + */ + Aggregation *GetOrSetDefault(const MetricAttributes &attributes, + std::function()> aggregation_callback) + { + std::lock_guard guard(lock_); + + auto it = hash_map_.find(attributes); + if (it != hash_map_.end()) + { + return it->second.get(); + } + + hash_map_[attributes] = std::move(aggregation_callback()); + return hash_map_[attributes].get(); + } + + /** + * Set the value for given key, overwriting the value if already present + */ + void Set(const MetricAttributes &attributes, std::unique_ptr value) + { + std::lock_guard guard(lock_); + hash_map_[attributes] = std::move(value); + } + + /** + * Iterate the hash to yield key and value stored in hash. + */ + bool GetAllEnteries( + nostd::function_ref callback) const + { + std::lock_guard guard(lock_); + for (auto &kv : hash_map_) + { + if (!callback(kv.first, *(kv.second.get()))) + { + return false; // callback is not prepared to consume data + } + } + return true; + } + + /** + * Return the size of hash. + */ + size_t Size() + { + std::lock_guard guard(lock_); + return hash_map_.size(); + } + +private: + std::unordered_map, AttributeHashGenerator> + hash_map_; + + mutable opentelemetry::common::SpinLockMutex lock_; +}; +} // namespace metrics + +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/metric_collector.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/metric_collector.h new file mode 100644 index 000000000..20372f520 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/metric_collector.h @@ -0,0 +1,57 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/data/metric_data.h" +# include "opentelemetry/sdk/metrics/export/metric_producer.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +class MetricReader; +class MeterContext; + +class CollectorHandle +{ +public: + virtual AggregationTemporality GetAggregationTemporality() noexcept = 0; +}; + +/** + * An internal opaque interface that the MetricReader receives as + * MetricProducer. It acts as the storage key to the internal metric stream + * state for each MetricReader. + */ + +class MetricCollector : public MetricProducer, public CollectorHandle +{ +public: + MetricCollector(std::shared_ptr &&context, + std::unique_ptr metric_reader); + + AggregationTemporality GetAggregationTemporality() noexcept override; + + /** + * The callback to be called for each metric exporter. This will only be those + * metrics that have been produced since the last time this method was called. + * + * @return a status of completion of method. + */ + bool Collect(nostd::function_ref callback) noexcept override; + + bool ForceFlush(std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept; + + bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept; + +private: + std::shared_ptr meter_context_; + std::shared_ptr metric_reader_; +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/metric_storage.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/metric_storage.h new file mode 100644 index 000000000..e0ba55cbf --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/metric_storage.h @@ -0,0 +1,86 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/common/key_value_iterable_view.h" +# include "opentelemetry/common/timestamp.h" +# include "opentelemetry/context/context.h" +# include "opentelemetry/sdk/metrics/data/metric_data.h" +# include "opentelemetry/sdk/metrics/instruments.h" +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +/* Represent the storage from which to collect the metrics */ +class CollectorHandle; + +class MetricStorage +{ +public: + /* collect the metrics from this storage */ + virtual bool Collect(CollectorHandle *collector, + nostd::span> collectors, + opentelemetry::common::SystemTimestamp sdk_start_ts, + opentelemetry::common::SystemTimestamp collection_ts, + nostd::function_ref callback) noexcept = 0; +}; + +class WritableMetricStorage +{ +public: + virtual void RecordLong(long value, const opentelemetry::context::Context &context) noexcept = 0; + + virtual void RecordLong(long value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept = 0; + + virtual void RecordDouble(double value, + const opentelemetry::context::Context &context) noexcept = 0; + + virtual void RecordDouble(double value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept = 0; + + virtual ~WritableMetricStorage() = default; +}; + +class NoopMetricStorage : public MetricStorage +{ +public: + bool Collect(CollectorHandle *collector, + nostd::span> collectors, + opentelemetry::common::SystemTimestamp sdk_start_ts, + opentelemetry::common::SystemTimestamp collection_ts, + nostd::function_ref callback) noexcept override + { + MetricData metric_data; + return callback(std::move(metric_data)); + } +}; + +class NoopWritableMetricStorage : public WritableMetricStorage +{ +public: + void RecordLong(long value, const opentelemetry::context::Context &context) noexcept = 0; + + void RecordLong(long value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept override + {} + + void RecordDouble(double value, const opentelemetry::context::Context &context) noexcept override + {} + + void RecordDouble(double value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept override + {} +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/multi_metric_storage.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/multi_metric_storage.h new file mode 100644 index 000000000..ceeafa040 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/multi_metric_storage.h @@ -0,0 +1,68 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/common/key_value_iterable_view.h" +# include "opentelemetry/sdk/metrics/instruments.h" +# include "opentelemetry/sdk/metrics/state/metric_storage.h" + +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +class MultiMetricStorage : public WritableMetricStorage +{ +public: + void AddStorage(std::shared_ptr storage) { storages_.push_back(storage); } + + virtual void RecordLong(long value, + const opentelemetry::context::Context &context) noexcept override + { + for (auto &s : storages_) + { + s->RecordLong(value, context); + } + } + + virtual void RecordLong(long value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept override + { + for (auto &s : storages_) + { + s->RecordLong(value, attributes, context); + } + } + + virtual void RecordDouble(double value, + const opentelemetry::context::Context &context) noexcept override + { + for (auto &s : storages_) + { + s->RecordDouble(value, context); + } + } + + virtual void RecordDouble(double value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept override + { + for (auto &s : storages_) + { + s->RecordDouble(value, attributes, context); + } + } + +private: + std::vector> storages_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h new file mode 100644 index 000000000..278f7dbe3 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/sync_metric_storage.h @@ -0,0 +1,119 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/common/key_value_iterable_view.h" +# include "opentelemetry/sdk/common/attributemap_hash.h" +# include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h" +# include "opentelemetry/sdk/metrics/exemplar/reservoir.h" +# include "opentelemetry/sdk/metrics/state/attributes_hashmap.h" +# include "opentelemetry/sdk/metrics/state/metric_collector.h" +# include "opentelemetry/sdk/metrics/state/metric_storage.h" + +# include "opentelemetry/sdk/metrics/state/temporal_metric_storage.h" +# include "opentelemetry/sdk/metrics/view/attributes_processor.h" + +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +class SyncMetricStorage : public MetricStorage, public WritableMetricStorage +{ + +public: + SyncMetricStorage(InstrumentDescriptor instrument_descriptor, + const AggregationType aggregation_type, + const AttributesProcessor *attributes_processor, + nostd::shared_ptr &&exemplar_reservoir) + : instrument_descriptor_(instrument_descriptor), + aggregation_type_{aggregation_type}, + attributes_hashmap_(new AttributesHashMap()), + attributes_processor_{attributes_processor}, + exemplar_reservoir_(exemplar_reservoir), + temporal_metric_storage_(instrument_descriptor) + + { + create_default_aggregation_ = [&]() -> std::unique_ptr { + return std::move( + DefaultAggregation::CreateAggregation(aggregation_type_, instrument_descriptor_)); + }; + } + + void RecordLong(long value, const opentelemetry::context::Context &context) noexcept override + { + if (instrument_descriptor_.value_type_ != InstrumentValueType::kLong) + { + return; + } + exemplar_reservoir_->OfferMeasurement(value, {}, context, std::chrono::system_clock::now()); + attributes_hashmap_->GetOrSetDefault({}, create_default_aggregation_)->Aggregate(value); + } + + void RecordLong(long value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept override + { + if (instrument_descriptor_.value_type_ != InstrumentValueType::kLong) + { + return; + } + + exemplar_reservoir_->OfferMeasurement(value, attributes, context, + std::chrono::system_clock::now()); + auto attr = attributes_processor_->process(attributes); + attributes_hashmap_->GetOrSetDefault(attr, create_default_aggregation_)->Aggregate(value); + } + + void RecordDouble(double value, const opentelemetry::context::Context &context) noexcept override + { + if (instrument_descriptor_.value_type_ != InstrumentValueType::kDouble) + { + return; + } + exemplar_reservoir_->OfferMeasurement(value, {}, context, std::chrono::system_clock::now()); + attributes_hashmap_->GetOrSetDefault({}, create_default_aggregation_)->Aggregate(value); + } + + void RecordDouble(double value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept override + { + exemplar_reservoir_->OfferMeasurement(value, attributes, context, + std::chrono::system_clock::now()); + if (instrument_descriptor_.value_type_ != InstrumentValueType::kDouble) + { + return; + } + exemplar_reservoir_->OfferMeasurement(value, attributes, context, + std::chrono::system_clock::now()); + auto attr = attributes_processor_->process(attributes); + attributes_hashmap_->GetOrSetDefault(attr, create_default_aggregation_)->Aggregate(value); + } + + bool Collect(CollectorHandle *collector, + nostd::span> collectors, + opentelemetry::common::SystemTimestamp sdk_start_ts, + opentelemetry::common::SystemTimestamp collection_ts, + nostd::function_ref callback) noexcept override; + +private: + InstrumentDescriptor instrument_descriptor_; + AggregationType aggregation_type_; + + // hashmap to maintain the metrics for delta collection (i.e, collection since last Collect call) + std::unique_ptr attributes_hashmap_; + const AttributesProcessor *attributes_processor_; + std::function()> create_default_aggregation_; + nostd::shared_ptr exemplar_reservoir_; + TemporalMetricStorage temporal_metric_storage_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/temporal_metric_storage.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/temporal_metric_storage.h new file mode 100644 index 000000000..16659c14f --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/state/temporal_metric_storage.h @@ -0,0 +1,50 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/state/attributes_hashmap.h" +# include "opentelemetry/sdk/metrics/state/metric_collector.h" + +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +struct LastReportedMetrics +{ + std::unique_ptr attributes_map; + opentelemetry::common::SystemTimestamp collection_ts; +}; + +class TemporalMetricStorage +{ +public: + TemporalMetricStorage(InstrumentDescriptor instrument_descriptor); + + bool buildMetrics(CollectorHandle *collector, + nostd::span> collectors, + opentelemetry::common::SystemTimestamp sdk_start_ts, + opentelemetry::common::SystemTimestamp collection_ts, + std::shared_ptr delta_metrics, + nostd::function_ref callback) noexcept; + +private: + InstrumentDescriptor instrument_descriptor_; + + // unreported metrics stash for all the collectors + std::unordered_map>> + unreported_metrics_; + // last reported metrics stash for all the collectors. + std::unordered_map last_reported_metrics_; + + // Lock while building metrics + mutable opentelemetry::common::SpinLockMutex lock_; +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/sync_instruments.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/sync_instruments.h new file mode 100644 index 000000000..f4c9bae32 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/sync_instruments.h @@ -0,0 +1,127 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/common/key_value_iterable.h" +# include "opentelemetry/metrics/sync_instruments.h" +# include "opentelemetry/nostd/string_view.h" +# include "opentelemetry/sdk/metrics/instruments.h" + +# include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +// forward declaration +class WritableMetricStorage; + +class Synchronous +{ +public: + Synchronous(InstrumentDescriptor instrument_descriptor, + std::unique_ptr storage) + : instrument_descriptor_(instrument_descriptor), storage_(std::move(storage)) + {} + +protected: + InstrumentDescriptor instrument_descriptor_; + std::unique_ptr storage_; +}; + +class LongCounter : public Synchronous, public opentelemetry::metrics::Counter +{ +public: + LongCounter(InstrumentDescriptor instrument_descriptor, + std::unique_ptr storage); + + void Add(long value, const opentelemetry::common::KeyValueIterable &attributes) noexcept override; + void Add(long value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept override; + + void Add(long value) noexcept override; + void Add(long value, const opentelemetry::context::Context &context) noexcept override; +}; + +class DoubleCounter : public Synchronous, public opentelemetry::metrics::Counter +{ + +public: + DoubleCounter(InstrumentDescriptor instrument_descriptor, + std::unique_ptr storage); + + void Add(double value, + const opentelemetry::common::KeyValueIterable &attributes) noexcept override; + void Add(double value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept override; + + void Add(double value) noexcept override; + void Add(double value, const opentelemetry::context::Context &context) noexcept override; +}; + +class LongUpDownCounter : public Synchronous, public opentelemetry::metrics::UpDownCounter +{ +public: + LongUpDownCounter(InstrumentDescriptor instrument_descriptor, + std::unique_ptr storage); + + void Add(long value, const opentelemetry::common::KeyValueIterable &attributes) noexcept override; + void Add(long value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept override; + + void Add(long value) noexcept override; + void Add(long value, const opentelemetry::context::Context &context) noexcept override; +}; + +class DoubleUpDownCounter : public Synchronous, public opentelemetry::metrics::UpDownCounter +{ +public: + DoubleUpDownCounter(InstrumentDescriptor instrument_descriptor, + std::unique_ptr storage); + + void Add(double value, + const opentelemetry::common::KeyValueIterable &attributes) noexcept override; + void Add(double value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept override; + + void Add(double value) noexcept override; + void Add(double value, const opentelemetry::context::Context &context) noexcept override; +}; + +class LongHistogram : public Synchronous, public opentelemetry::metrics::Histogram +{ +public: + LongHistogram(InstrumentDescriptor instrument_descriptor, + std::unique_ptr storage); + + void Record(long value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept override; + + void Record(long value, const opentelemetry::context::Context &context) noexcept override; +}; + +class DoubleHistogram : public Synchronous, public opentelemetry::metrics::Histogram +{ +public: + DoubleHistogram(InstrumentDescriptor instrument_descriptor, + std::unique_ptr storage); + + void Record(double value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept override; + + void Record(double value, const opentelemetry::context::Context &context) noexcept override; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/attributes_processor.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/attributes_processor.h new file mode 100644 index 000000000..d82607357 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/attributes_processor.h @@ -0,0 +1,81 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/common/attribute_utils.h" +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +using MetricAttributes = opentelemetry::sdk::common::OrderedAttributeMap; + +/** + * The AttributesProcessor is responsible for customizing which + * attribute(s) are to be reported as metrics dimension(s). + */ + +class AttributesProcessor +{ +public: + // Process the metric instrument attributes. + // @returns The processed attributes + virtual MetricAttributes process( + const opentelemetry::common::KeyValueIterable &attributes) const noexcept = 0; + + virtual ~AttributesProcessor() = default; +}; + +/** + * DefaultAttributesProcessor returns copy of input instrument attributes without + * any modification. + */ + +class DefaultAttributesProcessor : public AttributesProcessor +{ + MetricAttributes process( + const opentelemetry::common::KeyValueIterable &attributes) const noexcept override + { + MetricAttributes result(attributes); + return result; + } +}; + +/** + * FilteringAttributesProcessor filters by allowed attribute names and drops any names + * that are not in the allow list. + */ + +class FilteringAttributesProcessor : public AttributesProcessor +{ +public: + FilteringAttributesProcessor( + const std::unordered_map allowed_attribute_keys = {}) + : allowed_attribute_keys_(std::move(allowed_attribute_keys)) + {} + + MetricAttributes process( + const opentelemetry::common::KeyValueIterable &attributes) const noexcept override + { + MetricAttributes result; + attributes.ForEachKeyValue( + [&](nostd::string_view key, opentelemetry::common::AttributeValue value) noexcept { + if (allowed_attribute_keys_.find(key.data()) != allowed_attribute_keys_.end()) + { + result.SetAttribute(key, value); + return true; + } + return true; + }); + return result; + } + +private: + std::unordered_map allowed_attribute_keys_; +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/instrument_selector.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/instrument_selector.h new file mode 100644 index 000000000..e79a292c0 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/instrument_selector.h @@ -0,0 +1,36 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/nostd/string_view.h" +# include "opentelemetry/sdk/metrics/instruments.h" +# include "opentelemetry/sdk/metrics/view/predicate_factory.h" +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +class InstrumentSelector +{ +public: + InstrumentSelector(opentelemetry::sdk::metrics::InstrumentType instrument_type, + opentelemetry::nostd::string_view name) + : name_filter_{std::move(PredicateFactory::GetPredicate(name, PredicateType::kPattern))}, + instrument_type_{instrument_type} + {} + + // Returns name filter predicate. This shouldn't be deleted + const opentelemetry::sdk::metrics::Predicate *const GetNameFilter() { return name_filter_.get(); } + + // Returns instrument filter. + InstrumentType GetInstrumentType() { return instrument_type_; } + +private: + std::unique_ptr name_filter_; + opentelemetry::sdk::metrics::InstrumentType instrument_type_; +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/meter_selector.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/meter_selector.h new file mode 100644 index 000000000..d0bc07ccb --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/meter_selector.h @@ -0,0 +1,47 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/metrics/view/predicate_factory.h" +#ifndef ENABLE_METRICS_PREVIEW +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +class MeterSelector +{ +public: + MeterSelector(opentelemetry::nostd::string_view name, + opentelemetry::nostd::string_view version, + opentelemetry::nostd::string_view schema) + : name_filter_{std::move(PredicateFactory::GetPredicate(name, PredicateType::kExact))}, + version_filter_{std::move(PredicateFactory::GetPredicate(version, PredicateType::kExact))}, + schema_filter_{std::move(PredicateFactory::GetPredicate(schema, PredicateType::kExact))} + {} + + // Returns name filter predicate. This shouldn't be deleted + const opentelemetry::sdk::metrics::Predicate *const GetNameFilter() { return name_filter_.get(); } + + // Returns version filter predicate. This shouldn't be deleted + const opentelemetry::sdk::metrics::Predicate *const GetVersionFilter() + { + return version_filter_.get(); + } + + // Returns schema filter predicate. This shouldn't be deleted + const opentelemetry::sdk::metrics::Predicate *const GetSchemaFilter() + { + return schema_filter_.get(); + } + +private: + std::unique_ptr name_filter_; + std::unique_ptr version_filter_; + std::unique_ptr schema_filter_; +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/predicate.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/predicate.h new file mode 100644 index 000000000..72de2979a --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/predicate.h @@ -0,0 +1,82 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include +# if (__GNUC__ == 4 && (__GNUC_MINOR__ == 8 || __GNUC_MINOR__ == 9)) +# define HAVE_WORKING_REGEX 0 +# include "opentelemetry/sdk/common/global_log_handler.h" +# else +# include +# define HAVE_WORKING_REGEX 1 +# endif + +# include "opentelemetry/nostd/string_view.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +class Predicate +{ +public: + virtual ~Predicate() = default; + virtual bool Match(opentelemetry::nostd::string_view string) const noexcept = 0; +}; + +class PatternPredicate : public Predicate +{ +public: + PatternPredicate(opentelemetry::nostd::string_view pattern) : reg_key_{pattern.data()} {} + bool Match(opentelemetry::nostd::string_view str) const noexcept override + { +# if HAVE_WORKING_REGEX + return std::regex_match(str.data(), reg_key_); +# else + // TBD - Support regex match for GCC4.8 + OTEL_INTERNAL_LOG_ERROR( + "PatternPredicate::Match - failed. std::regex not fully supported for this compiler."); + return false; // not supported +# endif + } + +private: +# if HAVE_WORKING_REGEX + std::regex reg_key_; +# else + std::string reg_key_; +# endif +}; + +class ExactPredicate : public Predicate +{ +public: + ExactPredicate(opentelemetry::nostd::string_view pattern) : pattern_{pattern} {} + bool Match(opentelemetry::nostd::string_view str) const noexcept override + { + if (pattern_ == str) + { + return true; + } + return false; + } + +private: + std::string pattern_; +}; + +class MatchEverythingPattern : public Predicate +{ + bool Match(opentelemetry::nostd::string_view str) const noexcept override { return true; } +}; + +class MatchNothingPattern : public Predicate +{ + bool Match(opentelemetry::nostd::string_view str) const noexcept override { return false; } +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/predicate_factory.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/predicate_factory.h new file mode 100644 index 000000000..466fa76d5 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/predicate_factory.h @@ -0,0 +1,45 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/metrics/view/predicate.h" +#ifndef ENABLE_METRICS_PREVIEW +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +enum class PredicateType : uint8_t +{ + kPattern, + kExact +}; + +class PredicateFactory +{ +public: + static std::unique_ptr GetPredicate(opentelemetry::nostd::string_view pattern, + PredicateType type) + { + if ((type == PredicateType::kPattern && pattern == "*") || + (type == PredicateType::kExact && pattern == "")) + { + return std::move(std::unique_ptr(new MatchEverythingPattern())); + } + if (type == PredicateType::kPattern) + { + return std::move(std::unique_ptr(new PatternPredicate(pattern))); + } + if (type == PredicateType::kExact) + { + return std::move(std::unique_ptr(new ExactPredicate(pattern))); + } + return std::move(std::unique_ptr(new MatchNothingPattern())); + } +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/view.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/view.h new file mode 100644 index 000000000..3cd9f850e --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/view.h @@ -0,0 +1,57 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/nostd/string_view.h" +# include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h" +# include "opentelemetry/sdk/metrics/instruments.h" +# include "opentelemetry/sdk/metrics/view/attributes_processor.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +/** + * View defines the interface to allow SDK user to + * customize the metrics before exported. + */ + +class View +{ +public: + View(const std::string &name, + const std::string &description = "", + AggregationType aggregation_type = AggregationType::kDefault, + std::unique_ptr attributes_processor = + std::unique_ptr( + new opentelemetry::sdk::metrics::DefaultAttributesProcessor())) + : name_(name), + description_(description), + aggregation_type_{aggregation_type}, + attributes_processor_{std::move(attributes_processor)} + {} + + virtual std::string GetName() const noexcept { return name_; } + + virtual std::string GetDescription() const noexcept { return description_; } + + virtual AggregationType GetAggregationType() const noexcept { return aggregation_type_; } + + virtual const opentelemetry::sdk::metrics::AttributesProcessor &GetAttributesProcessor() + const noexcept + { + return *attributes_processor_.get(); + } + +private: + std::string name_; + std::string description_; + AggregationType aggregation_type_; + std::unique_ptr attributes_processor_; +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/view_registry.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/view_registry.h new file mode 100644 index 000000000..795049dd9 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/metrics/view/view_registry.h @@ -0,0 +1,102 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include +# include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h" +# include "opentelemetry/sdk/metrics/view/instrument_selector.h" +# include "opentelemetry/sdk/metrics/view/meter_selector.h" +# include "opentelemetry/sdk/metrics/view/view.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +struct RegisteredView +{ + RegisteredView( + std::unique_ptr instrument_selector, + std::unique_ptr meter_selector, + std::unique_ptr view) + : instrument_selector_{std::move(instrument_selector)}, + meter_selector_{std::move(meter_selector)}, + view_{std::move(view)} + {} + std::unique_ptr instrument_selector_; + std::unique_ptr meter_selector_; + std::unique_ptr view_; +}; + +class ViewRegistry +{ +public: + void AddView(std::unique_ptr instrument_selector, + std::unique_ptr meter_selector, + std::unique_ptr view) + { + // TBD - thread-safe ? + + auto registered_view = std::unique_ptr(new RegisteredView{ + std::move(instrument_selector), std::move(meter_selector), std::move(view)}); + registered_views_.push_back(std::move(registered_view)); + } + + bool FindViews(const opentelemetry::sdk::metrics::InstrumentDescriptor &instrument_descriptor, + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + &instrumentation_library, + nostd::function_ref callback) const + { + bool found = false; + for (auto const ®istered_view : registered_views_) + { + if (MatchMeter(registered_view->meter_selector_.get(), instrumentation_library) && + MatchInstrument(registered_view->instrument_selector_.get(), instrument_descriptor)) + { + found = true; + if (!callback(*(registered_view->view_.get()))) + { + return false; + } + } + } + // return default view if none found; + if (!found) + { + static View view("otel-default-view"); + if (!callback(view)) + { + return false; + } + } + return true; + } + + ~ViewRegistry() = default; + +private: + std::vector> registered_views_; + static bool MatchMeter(opentelemetry::sdk::metrics::MeterSelector *selector, + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + &instrumentation_library) + { + return selector->GetNameFilter()->Match(instrumentation_library.GetName()) && + (instrumentation_library.GetVersion().size() == 0 || + selector->GetVersionFilter()->Match(instrumentation_library.GetVersion())) && + (instrumentation_library.GetSchemaURL().size() == 0 || + selector->GetSchemaFilter()->Match(instrumentation_library.GetSchemaURL())); + } + + static bool MatchInstrument( + opentelemetry::sdk::metrics::InstrumentSelector *selector, + const opentelemetry::sdk::metrics::InstrumentDescriptor &instrument_descriptor) + { + return selector->GetNameFilter()->Match(instrument_descriptor.name_) && + (selector->GetInstrumentType() == instrument_descriptor.type_); + } +}; +}; // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/resource/experimental_semantic_conventions.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/resource/experimental_semantic_conventions.h new file mode 100644 index 000000000..d9dc3630f --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/resource/experimental_semantic_conventions.h @@ -0,0 +1,141 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// NOTE: +// This implementation is based on the experimental specs for resource semantic convention as +// defined here: +// https://github.com/open-telemetry/opentelemetry-specification/tree/v1.0.0/specification/resource/semantic_conventions +// and MAY will change in future. + +#pragma once + +#include +#include + +#include "opentelemetry/common/string_util.h" +#include "opentelemetry/version.h" + +#define OTEL_GET_RESOURCE_ATTR(name) \ + opentelemetry::sdk::resource::attr(OTEL_CPP_CONST_HASHCODE(name)) + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace resource +{ + +static const std::unordered_map attribute_ids = { + {OTEL_CPP_CONST_HASHCODE(AttrServiceName), "service.name"}, + {OTEL_CPP_CONST_HASHCODE(AttrServiceNamespace), "service.namespace"}, + {OTEL_CPP_CONST_HASHCODE(AttrServiceInstance), "service.instance.id"}, + {OTEL_CPP_CONST_HASHCODE(AttrServiceVersion), "service.version"}, + + // telemetry attributes + {OTEL_CPP_CONST_HASHCODE(AttrTelemetrySdkName), "telemetry.sdk.name"}, + {OTEL_CPP_CONST_HASHCODE(AttrTelemetrySdkLanguage), "telemetry.sdk.language"}, + {OTEL_CPP_CONST_HASHCODE(AttrTelemetrySdkVersion), "telemetry.sdk.version"}, + {OTEL_CPP_CONST_HASHCODE(AttrTelemetryAutoVersion), "telemetry.auto.version"}, + + // compute unit: container attributes + {OTEL_CPP_CONST_HASHCODE(AttrContainerName), "container.name"}, + {OTEL_CPP_CONST_HASHCODE(AttrContainerId), "container.id"}, + {OTEL_CPP_CONST_HASHCODE(AttrContainerRuntime), "container.runtime"}, + {OTEL_CPP_CONST_HASHCODE(AttrContainerImageName), "container.image.name"}, + {OTEL_CPP_CONST_HASHCODE(AttrContainerImageTag), "container.image.tag"}, + + // compute unit: faas attributes + {OTEL_CPP_CONST_HASHCODE(AttrFaasName), "faas.name"}, + {OTEL_CPP_CONST_HASHCODE(AttrFaasId), "faas.id"}, + {OTEL_CPP_CONST_HASHCODE(AttrFaasVersion), "faas.version"}, + {OTEL_CPP_CONST_HASHCODE(AttrFaasInstance), "faas.instance"}, + {OTEL_CPP_CONST_HASHCODE(AttrFaasMaxMemory), "faas.max_memory"}, + + // compute unit : process attributes + {OTEL_CPP_CONST_HASHCODE(AttrProcessId), "process.pid"}, + {OTEL_CPP_CONST_HASHCODE(AttrProcessExecutableName), "process.executable.name"}, + {OTEL_CPP_CONST_HASHCODE(AttrProcessExecutablePath), "process.executable.path"}, + {OTEL_CPP_CONST_HASHCODE(AttrProcessCommand), "process.command"}, + {OTEL_CPP_CONST_HASHCODE(AttrProcessCommandLine), "process.command_line"}, + {OTEL_CPP_CONST_HASHCODE(AttrProcessCommandArgs), "process.command_args"}, + {OTEL_CPP_CONST_HASHCODE(AttrProcessOwner), "process.owner"}, + + // compute : process runtimes + {OTEL_CPP_CONST_HASHCODE(AttrProcessRuntimeName), "process.runtime.name"}, + {OTEL_CPP_CONST_HASHCODE(AttrProcessRuntimeVersion), "process.runtime.version"}, + {OTEL_CPP_CONST_HASHCODE(AttrProcessRuntimeDescription), "process.runtime.description"}, + + // compute unit : WebEngine + {OTEL_CPP_CONST_HASHCODE(AttrWebEngineName), "webengine.name"}, + {OTEL_CPP_CONST_HASHCODE(AttrWebEngineVersion), "webengine.version"}, + {OTEL_CPP_CONST_HASHCODE(AttrWebEngineDescription), "webengine.description"}, + + // compute instance : host + {OTEL_CPP_CONST_HASHCODE(AttrHostId), "host.id"}, + {OTEL_CPP_CONST_HASHCODE(AttrHostName), "host.name"}, + {OTEL_CPP_CONST_HASHCODE(AttrHostType), "host.type"}, + {OTEL_CPP_CONST_HASHCODE(AttrHostArch), "host.arch"}, + {OTEL_CPP_CONST_HASHCODE(AttrHostImageName), "host.image.name"}, + {OTEL_CPP_CONST_HASHCODE(AttrHostImageId), "host.image.id"}, + {OTEL_CPP_CONST_HASHCODE(AttrHostImageVersion), "host.image.version"}, + + // env os attributes + {OTEL_CPP_CONST_HASHCODE(AttrOsType), "os.type"}, + {OTEL_CPP_CONST_HASHCODE(AttrOsDescription), "os.description"}, + {OTEL_CPP_CONST_HASHCODE(AttrOsName), "os.name"}, + {OTEL_CPP_CONST_HASHCODE(AttrOsVersion), "os.version"}, + + // env device attributes + {OTEL_CPP_CONST_HASHCODE(AttrDeviceId), "device.id"}, + {OTEL_CPP_CONST_HASHCODE(AttrDeviceModelIdentifier), "device.model.identifier"}, + {OTEL_CPP_CONST_HASHCODE(AttrDeviceModelName), "device.model.name"}, + + // env cloud + {OTEL_CPP_CONST_HASHCODE(AttrCloudProvider), "cloud.provider"}, + {OTEL_CPP_CONST_HASHCODE(AttrCloudAccountId), "cloud.account.id"}, + {OTEL_CPP_CONST_HASHCODE(AttrCloudRegion), "cloud.region"}, + {OTEL_CPP_CONST_HASHCODE(AttrCloudAvailabilityZone), "cloud.availability_zone"}, + {OTEL_CPP_CONST_HASHCODE(AttrCloudPlatform), "cloud.platform"}, + + // env deployment + {OTEL_CPP_CONST_HASHCODE(AttrDeploymentEnvironment), "deployment.environment"}, + + // env kubernetes + // - cluster + {OTEL_CPP_CONST_HASHCODE(AttrK8sClusterName), "k8s.cluster.name"}, + // - node + {OTEL_CPP_CONST_HASHCODE(AttrK8sNodeName), "k8s.node.name"}, + {OTEL_CPP_CONST_HASHCODE(AttrK8sNodeUid), "k8s.node.uid"}, + // - namespace + {OTEL_CPP_CONST_HASHCODE(AttrK8sNamespaceName), "k8s.namespace.name"}, + // - pod + {OTEL_CPP_CONST_HASHCODE(AttrK8sPodUid), "k8s.pod.uid"}, + {OTEL_CPP_CONST_HASHCODE(AttrK8sPodName), "k8s.pod.name"}, + // - container + {OTEL_CPP_CONST_HASHCODE(AttrK8sContainerName), "k8s.container.name"}, + // - replicaset + {OTEL_CPP_CONST_HASHCODE(AttrK8sReplicaSetUid), "k8s.replicaset.uid"}, + {OTEL_CPP_CONST_HASHCODE(AttrK8sReplicaSetName), "k8s.replicaset.name"}, + // - deployment + {OTEL_CPP_CONST_HASHCODE(AttrK8sDeploymentUid), "k8s.deployment.uid"}, + {OTEL_CPP_CONST_HASHCODE(AttrK8sDeploymentName), "k8s.deployment.name"}, + // - stateful-set + {OTEL_CPP_CONST_HASHCODE(AttrK8sStatefulSetUid), "k8s.statefulset.uid"}, + {OTEL_CPP_CONST_HASHCODE(AttrK8sStatefulSetName), "k8s.statefulset.name"}, + // - daemon set + {OTEL_CPP_CONST_HASHCODE(AttrK8sDaemonSetUid), "k8s.daemonset.uid"}, + {OTEL_CPP_CONST_HASHCODE(AttrK8sDaemonSetName), "k8s.daemonset.name"}, + // - job + {OTEL_CPP_CONST_HASHCODE(AttrK8sJobUid), "k8s.job.uid"}, + {OTEL_CPP_CONST_HASHCODE(AttrK8sJobName), "k8s.job.name"}, + // - cronjob + {OTEL_CPP_CONST_HASHCODE(AttrCronjobUid), "k8s.cronjob.id"}, + {OTEL_CPP_CONST_HASHCODE(AttrCronjobName), "k8s.cronjob.name"}}; + +// function to generate hash code for semantic conventions attributes. +inline const char *attr(uint32_t attr) +{ + return (attribute_ids.find(attr) != attribute_ids.end()) ? attribute_ids.at(attr) : ""; +} +} // namespace resource +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/resource/resource.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/resource/resource.h new file mode 100644 index 000000000..120e871ab --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/resource/resource.h @@ -0,0 +1,86 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/common/key_value_iterable_view.h" + +#include "opentelemetry/sdk/common/attribute_utils.h" +#include "opentelemetry/sdk/resource/resource_detector.h" +#include "opentelemetry/sdk/version/version.h" +#include "opentelemetry/version.h" + +#include +#include +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace resource +{ + +using ResourceAttributes = opentelemetry::sdk::common::AttributeMap; + +class Resource +{ +public: + Resource(const Resource &) = default; + + const ResourceAttributes &GetAttributes() const noexcept; + const std::string &GetSchemaURL() const noexcept; + + /** + * Returns a new, merged {@link Resource} by merging the current Resource + * with the other Resource. In case of a collision, current Resource takes + * precedence. + * + * @param other the Resource that will be merged with this. + * @returns the newly merged Resource. + */ + + Resource Merge(const Resource &other) noexcept; + + /** + * Returns a newly created Resource with the specified attributes. + * It adds (merge) SDK attributes and OTEL attributes before returning. + * @param attributes for this resource + * @returns the newly created Resource. + */ + + static Resource Create(const ResourceAttributes &attributes, + const std::string &schema_url = std::string{}); + + /** + * Returns an Empty resource. + */ + + static Resource &GetEmpty(); + + /** + * Returns a Resource that indentifies the SDK in use. + */ + + static Resource &GetDefault(); + +protected: + /** + * The constructor is protected and only for use internally by the class and + * inside ResourceDetector class. + * Users should use the Create factory method to obtain a Resource + * instance. + */ + Resource(const ResourceAttributes &attributes = ResourceAttributes(), + const std::string &schema_url = std::string{}) noexcept; + +private: + ResourceAttributes attributes_; + std::string schema_url_; + + friend class OTELResourceDetector; +}; + +} // namespace resource +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/resource/resource_detector.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/resource/resource_detector.h new file mode 100644 index 000000000..5f9d904af --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/resource/resource_detector.h @@ -0,0 +1,38 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/nostd/unique_ptr.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace resource +{ + +class Resource; + +/** + * Interface for a Resource Detector + */ +class ResourceDetector +{ +public: + virtual Resource Detect() = 0; +}; + +/** + * OTelResourceDetector to detect the presence of and create a Resource + * from the OTEL_RESOURCE_ATTRIBUTES environment variable. + */ +class OTELResourceDetector : public ResourceDetector +{ +public: + Resource Detect() noexcept override; +}; + +} // namespace resource +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/batch_span_processor.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/batch_span_processor.h new file mode 100644 index 000000000..d25ff2d95 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/batch_span_processor.h @@ -0,0 +1,158 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/sdk/common/circular_buffer.h" +#include "opentelemetry/sdk/trace/exporter.h" +#include "opentelemetry/sdk/trace/processor.h" + +#include +#include +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ + +namespace trace +{ + +/** + * Struct to hold batch SpanProcessor options. + */ +struct BatchSpanProcessorOptions +{ + /** + * The maximum buffer/queue size. After the size is reached, spans are + * dropped. + */ + size_t max_queue_size = 2048; + + /* The time interval between two consecutive exports. */ + std::chrono::milliseconds schedule_delay_millis = std::chrono::milliseconds(5000); + + /** + * The maximum batch size of every export. It must be smaller or + * equal to max_queue_size. + */ + size_t max_export_batch_size = 512; +}; + +/** + * This is an implementation of the SpanProcessor which creates batches of finished spans and passes + * the export-friendly span data representations to the configured SpanExporter. + */ +class BatchSpanProcessor : public SpanProcessor +{ +public: + /** + * Creates a batch span processor by configuring the specified exporter and other parameters + * as per the official, language-agnostic opentelemetry specs. + * + * @param exporter - The backend exporter to pass the ended spans to. + * @param options - The batch SpanProcessor options. + */ + BatchSpanProcessor(std::unique_ptr &&exporter, + const BatchSpanProcessorOptions &options); + + /** + * Requests a Recordable(Span) from the configured exporter. + * + * @return A recordable generated by the backend exporter + */ + std::unique_ptr MakeRecordable() noexcept override; + + /** + * Called when a span is started. + * + * NOTE: This method is a no-op. + * + * @param span - The span that just started + * @param parent_context - The parent context of the span that just started + */ + void OnStart(Recordable &span, + const opentelemetry::trace::SpanContext &parent_context) noexcept override; + + /** + * Called when a span ends. + * + * @param span - A recordable for a span that just ended + */ + void OnEnd(std::unique_ptr &&span) noexcept override; + + /** + * Export all ended spans that have not been exported yet. + * + * NOTE: Timeout functionality not supported yet. + */ + bool ForceFlush( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override; + + /** + * Shuts down the processor and does any cleanup required. Completely drains the buffer/queue of + * all its ended spans and passes them to the exporter. Any subsequent calls to OnStart, OnEnd, + * ForceFlush or Shutdown will return immediately without doing anything. + * + * NOTE: Timeout functionality not supported yet. + */ + bool Shutdown( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override; + + /** + * Class destructor which invokes the Shutdown() method. The Shutdown() method is supposed to be + * invoked when the Tracer is shutdown (as per other languages), but the C++ Tracer only takes + * shared ownership of the processor, and thus doesn't call Shutdown (as the processor might be + * shared with other Tracers). + */ + ~BatchSpanProcessor(); + +private: + /** + * The background routine performed by the worker thread. + */ + void DoBackgroundWork(); + + /** + * Exports all ended spans to the configured exporter. + * + * @param was_force_flush_called - A flag to check if the current export is the result + * of a call to ForceFlush method. If true, then we have to + * notify the main thread to wake it up in the ForceFlush + * method. + */ + void Export(const bool was_for_flush_called); + + /** + * Called when Shutdown() is invoked. Completely drains the queue of all its ended spans and + * passes them to the exporter. + */ + void DrainQueue(); + + /* The configured backend exporter */ + std::unique_ptr exporter_; + + /* Configurable parameters as per the official specs */ + const size_t max_queue_size_; + const std::chrono::milliseconds schedule_delay_millis_; + const size_t max_export_batch_size_; + + /* Synchronization primitives */ + std::condition_variable cv_, force_flush_cv_; + std::mutex cv_m_, force_flush_cv_m_, shutdown_m_; + + /* The buffer/queue to which the ended spans are added */ + common::CircularBuffer buffer_; + + /* Important boolean flags to handle the workflow of the processor */ + std::atomic is_shutdown_{false}; + std::atomic is_force_flush_{false}; + std::atomic is_force_flush_notified_{false}; + + /* The background worker thread */ + std::thread worker_thread_; +}; + +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/exporter.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/exporter.h new file mode 100644 index 000000000..5826b5f45 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/exporter.h @@ -0,0 +1,55 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/sdk/common/exporter_utils.h" +#include "opentelemetry/sdk/trace/recordable.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +/** + * SpanExporter defines the interface that protocol-specific span exporters must + * implement. + */ +class SpanExporter +{ +public: + virtual ~SpanExporter() = default; + + /** + * Create a span recordable. This object will be used to record span data and + * will subsequently be passed to SpanExporter::Export. Vendors can implement + * custom recordables or use the default SpanData recordable provided by the + * SDK. + * @return a newly initialized Recordable object + * + * Note: This method must be callable from multiple threads. + */ + virtual std::unique_ptr MakeRecordable() noexcept = 0; + + /** + * Exports a batch of span recordables. This method must not be called + * concurrently for the same exporter instance. + * @param spans a span of unique pointers to span recordables + */ + virtual sdk::common::ExportResult Export( + const nostd::span> + &spans) noexcept = 0; + + /** + * Shut down the exporter. + * @param timeout an optional timeout. + * @return return the status of the operation. + */ + virtual bool Shutdown( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept = 0; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/id_generator.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/id_generator.h new file mode 100644 index 000000000..da17effae --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/id_generator.h @@ -0,0 +1,31 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#include "opentelemetry/trace/span_id.h" +#include "opentelemetry/trace/trace_id.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ + +/** IdGenerator provides an interface for generating Trace Id and Span Id */ +class IdGenerator +{ + +public: + virtual ~IdGenerator() = default; + + /** Returns a SpanId represented by opaque 128-bit trace identifier */ + virtual opentelemetry::trace::SpanId GenerateSpanId() noexcept = 0; + + /** Returns a TraceId represented by opaque 64-bit trace identifier */ + virtual opentelemetry::trace::TraceId GenerateTraceId() noexcept = 0; +}; +} // namespace trace + +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/multi_recordable.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/multi_recordable.h new file mode 100644 index 000000000..be97dddc9 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/multi_recordable.h @@ -0,0 +1,161 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/common/timestamp.h" +#include "opentelemetry/sdk/trace/processor.h" +#include "opentelemetry/sdk/trace/recordable.h" +#include "opentelemetry/version.h" + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +namespace +{ +std::size_t MakeKey(const SpanProcessor &processor) +{ + return reinterpret_cast(&processor); +} + +} // namespace + +class MultiRecordable : public Recordable +{ +public: + void AddRecordable(const SpanProcessor &processor, + std::unique_ptr recordable) noexcept + { + recordables_[MakeKey(processor)] = std::move(recordable); + } + + const std::unique_ptr &GetRecordable(const SpanProcessor &processor) const noexcept + { + // TODO - return nullptr ref on failed lookup? + auto i = recordables_.find(MakeKey(processor)); + if (i != recordables_.end()) + { + return i->second; + } + static std::unique_ptr empty(nullptr); + return empty; + } + + std::unique_ptr ReleaseRecordable(const SpanProcessor &processor) noexcept + { + auto i = recordables_.find(MakeKey(processor)); + if (i != recordables_.end()) + { + std::unique_ptr result(i->second.release()); + recordables_.erase(MakeKey(processor)); + return result; + } + return std::unique_ptr(nullptr); + } + + void SetIdentity(const opentelemetry::trace::SpanContext &span_context, + opentelemetry::trace::SpanId parent_span_id) noexcept override + { + for (auto &recordable : recordables_) + { + recordable.second->SetIdentity(span_context, parent_span_id); + } + } + + void SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept override + { + for (auto &recordable : recordables_) + { + recordable.second->SetAttribute(key, value); + } + } + + void AddEvent(nostd::string_view name, + opentelemetry::common::SystemTimestamp timestamp, + const opentelemetry::common::KeyValueIterable &attributes) noexcept override + { + + for (auto &recordable : recordables_) + { + recordable.second->AddEvent(name, timestamp, attributes); + } + } + + void AddLink(const opentelemetry::trace::SpanContext &span_context, + const opentelemetry::common::KeyValueIterable &attributes) noexcept override + { + for (auto &recordable : recordables_) + { + recordable.second->AddLink(span_context, attributes); + } + } + + void SetStatus(opentelemetry::trace::StatusCode code, + nostd::string_view description) noexcept override + { + for (auto &recordable : recordables_) + { + recordable.second->SetStatus(code, description); + } + } + + void SetName(nostd::string_view name) noexcept override + { + for (auto &recordable : recordables_) + { + recordable.second->SetName(name); + } + } + + void SetSpanKind(opentelemetry::trace::SpanKind span_kind) noexcept override + { + for (auto &recordable : recordables_) + { + recordable.second->SetSpanKind(span_kind); + } + } + + void SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept override + { + for (auto &recordable : recordables_) + { + recordable.second->SetResource(resource); + } + } + + void SetStartTime(opentelemetry::common::SystemTimestamp start_time) noexcept override + { + for (auto &recordable : recordables_) + { + recordable.second->SetStartTime(start_time); + } + } + + void SetDuration(std::chrono::nanoseconds duration) noexcept override + { + for (auto &recordable : recordables_) + { + recordable.second->SetDuration(duration); + } + } + + void SetInstrumentationLibrary( + const InstrumentationLibrary &instrumentation_library) noexcept override + { + for (auto &recordable : recordables_) + { + recordable.second->SetInstrumentationLibrary(instrumentation_library); + } + } + +private: + std::map> recordables_; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/multi_span_processor.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/multi_span_processor.h new file mode 100644 index 000000000..8463ad520 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/multi_span_processor.h @@ -0,0 +1,187 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +#include "opentelemetry/sdk/trace/multi_recordable.h" +#include "opentelemetry/sdk/trace/processor.h" + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ + +/** Instantiation options. */ +struct MultiSpanProcessorOptions +{}; + +/** + * Span processor allow hooks for span start and end method invocations. + * + * Built-in span processors are responsible for batching and conversion of + * spans to exportable representation and passing batches to exporters. + */ +class MultiSpanProcessor : public SpanProcessor +{ +public: + MultiSpanProcessor(std::vector> &&processors) + : head_(nullptr), tail_(nullptr), count_(0) + { + for (auto &processor : processors) + { + AddProcessor(std::move(processor)); + } + } + + void AddProcessor(std::unique_ptr &&processor) + { + // Add preocessor to end of the list. + if (processor) + { + ProcessorNode *pNode = new ProcessorNode(std::move(processor), tail_); + if (count_ > 0) + { + tail_->next_ = pNode; + tail_ = pNode; + } + else + { + head_ = tail_ = pNode; + } + count_++; + } + } + + std::unique_ptr MakeRecordable() noexcept override + { + auto recordable = std::unique_ptr(new MultiRecordable); + auto multi_recordable = static_cast(recordable.get()); + ProcessorNode *node = head_; + while (node != nullptr) + { + auto processor = node->value_.get(); + multi_recordable->AddRecordable(*processor, processor->MakeRecordable()); + node = node->next_; + } + return recordable; + } + + virtual void OnStart(Recordable &span, + const opentelemetry::trace::SpanContext &parent_context) noexcept override + { + auto multi_recordable = static_cast(&span); + ProcessorNode *node = head_; + while (node != nullptr) + { + auto processor = node->value_.get(); + auto &recordable = multi_recordable->GetRecordable(*processor); + if (recordable != nullptr) + { + processor->OnStart(*recordable, parent_context); + } + node = node->next_; + } + } + + virtual void OnEnd(std::unique_ptr &&span) noexcept override + { + auto multi_recordable = static_cast(span.release()); + ProcessorNode *node = head_; + while (node != nullptr) + { + auto processor = node->value_.get(); + auto recordable = multi_recordable->ReleaseRecordable(*processor); + if (recordable != nullptr) + { + processor->OnEnd(std::move(recordable)); + } + node = node->next_; + } + delete multi_recordable; + } + + bool ForceFlush( + std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override + { + bool result = true; + ProcessorNode *node = head_; + while (node != nullptr) + { + auto processor = node->value_.get(); + result |= processor->ForceFlush(timeout); + node = node->next_; + } + return result; + } + + bool Shutdown( + std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override + { + bool result = true; + ProcessorNode *node = head_; + while (node != nullptr) + { + auto processor = node->value_.get(); + result |= processor->Shutdown(timeout); + node = node->next_; + } + return result; + } + + ~MultiSpanProcessor() + { + Shutdown(); + Cleanup(); + } + +private: + struct ProcessorNode + { + std::unique_ptr value_; + ProcessorNode *next_, *prev_; + ProcessorNode(std::unique_ptr &&value, + ProcessorNode *prev = nullptr, + ProcessorNode *next = nullptr) + : value_(std::move(value)), next_(next), prev_(prev) + {} + }; + + void Cleanup() + { + if (count_) + { + ProcessorNode *node = tail_; + while (node != nullptr) + { + if (node->next_ != nullptr) + { + delete node->next_; + node->next_ = nullptr; + } + if (node->prev_ != nullptr) + { + node = node->prev_; + } + else + { + delete node; + node = nullptr; + } + } + head_ = tail_ = nullptr; + count_ = 0; + } + } + + ProcessorNode *head_, *tail_; + size_t count_; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/processor.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/processor.h new file mode 100644 index 000000000..17ac0e59a --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/processor.h @@ -0,0 +1,70 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include "opentelemetry/sdk/trace/recordable.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +/** + * Span processor allow hooks for span start and end method invocations. + * + * Built-in span processors are responsible for batching and conversion of + * spans to exportable representation and passing batches to exporters. + */ +class SpanProcessor +{ +public: + virtual ~SpanProcessor() = default; + + /** + * Create a span recordable. This requests a new span recordable from the + * associated exporter. + * @return a newly initialized recordable + * + * Note: This method must be callable from multiple threads. + */ + virtual std::unique_ptr MakeRecordable() noexcept = 0; + + /** + * OnStart is called when a span is started. + * @param span a recordable for a span that was just started + * @param parent_context The parent context of the span that just started + */ + virtual void OnStart(Recordable &span, + const opentelemetry::trace::SpanContext &parent_context) noexcept = 0; + + /** + * OnEnd is called when a span is ended. + * @param span a recordable for a span that was ended + */ + virtual void OnEnd(std::unique_ptr &&span) noexcept = 0; + + /** + * Export all ended spans that have not yet been exported. + * @param timeout an optional timeout, the default timeout of 0 means that no + * timeout is applied. + */ + virtual bool ForceFlush( + std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept = 0; + + /** + * Shut down the processor and do any cleanup required. Ended spans are + * exported before shutdown. After the call to Shutdown, subsequent calls to + * OnStart, OnEnd, ForceFlush or Shutdown will return immediately without + * doing anything. + * @param timeout an optional timeout, the default timeout of 0 means that no + * timeout is applied. + */ + virtual bool Shutdown( + std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept = 0; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/random_id_generator.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/random_id_generator.h new file mode 100644 index 000000000..908f1b850 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/random_id_generator.h @@ -0,0 +1,24 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#include "opentelemetry/sdk/trace/id_generator.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ + +class RandomIdGenerator : public IdGenerator +{ +public: + opentelemetry::trace::SpanId GenerateSpanId() noexcept override; + + opentelemetry::trace::TraceId GenerateTraceId() noexcept override; +}; + +} // namespace trace + +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/recordable.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/recordable.h new file mode 100644 index 000000000..b92a3ff06 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/recordable.h @@ -0,0 +1,152 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/common/key_value_iterable.h" +#include "opentelemetry/common/timestamp.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/common/empty_attributes.h" +#include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h" +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/trace/canonical_code.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/span_context.h" +#include "opentelemetry/trace/span_id.h" +#include "opentelemetry/trace/trace_id.h" +#include "opentelemetry/version.h" + +#include + +// TODO: Create generic short pattern for opentelemetry::common and opentelemetry::trace + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ + +using namespace opentelemetry::sdk::instrumentationlibrary; + +/** + * Maintains a representation of a span in a format that can be processed by a recorder. + * + * This class is thread-compatible. + */ +class Recordable +{ +public: + virtual ~Recordable() = default; + + /** + * Set the span context and parent span id + * @param span_context the span context to set + * @param parent_span_id the parent span id to set + */ + virtual void SetIdentity(const opentelemetry::trace::SpanContext &span_context, + opentelemetry::trace::SpanId parent_span_id) noexcept = 0; + + /** + * Set an attribute of a span. + * @param name the name of the attribute + * @param value the attribute value + */ + virtual void SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept = 0; + + /** + * Add an event to a span. + * @param name the name of the event + * @param timestamp the timestamp of the event + * @param attributes the attributes associated with the event + */ + virtual void AddEvent(nostd::string_view name, + opentelemetry::common::SystemTimestamp timestamp, + const opentelemetry::common::KeyValueIterable &attributes) noexcept = 0; + + /** + * Add an event to a span with default timestamp and attributes. + * @param name the name of the event + */ + void AddEvent(nostd::string_view name) + { + AddEvent(name, opentelemetry::common::SystemTimestamp(std::chrono::system_clock::now()), + opentelemetry::sdk::GetEmptyAttributes()); + } + + /** + * Add an event to a span with default (empty) attributes. + * @param name the name of the event + * @param timestamp the timestamp of the event + */ + void AddEvent(nostd::string_view name, opentelemetry::common::SystemTimestamp timestamp) + { + AddEvent(name, timestamp, opentelemetry::sdk::GetEmptyAttributes()); + } + + /** + * Add a link to a span. + * @param span_context the span context of the linked span + * @param attributes the attributes associated with the link + */ + virtual void AddLink(const opentelemetry::trace::SpanContext &span_context, + const opentelemetry::common::KeyValueIterable &attributes) noexcept = 0; + + /** + * Add a link to a span with default (empty) attributes. + * @param span_context the span context of the linked span + */ + void AddLink(opentelemetry::trace::SpanContext span_context) + { + AddLink(span_context, opentelemetry::sdk::GetEmptyAttributes()); + } + + /** + * Set the status of the span. + * @param code the status code + * @param description a description of the status + */ + virtual void SetStatus(opentelemetry::trace::StatusCode code, + nostd::string_view description) noexcept = 0; + + /** + * Set the name of the span. + * @param name the name to set + */ + virtual void SetName(nostd::string_view name) noexcept = 0; + + /** + * Set the spankind of the span. + * @param span_kind the spankind to set + */ + virtual void SetSpanKind(opentelemetry::trace::SpanKind span_kind) noexcept = 0; + + /** + * Set Resource of the span + * @param Resource the resource to set + */ + virtual void SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept = 0; + + /** + * Set the start time of the span. + * @param start_time the start time to set + */ + virtual void SetStartTime(opentelemetry::common::SystemTimestamp start_time) noexcept = 0; + + /** + * Set the duration of the span. + * @param duration the duration to set + */ + virtual void SetDuration(std::chrono::nanoseconds duration) noexcept = 0; + + /** + * Set the instrumentation library of the span. + * @param instrumentation_library the instrumentation library to set + */ + virtual void SetInstrumentationLibrary( + const InstrumentationLibrary &instrumentation_library) noexcept = 0; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/sampler.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/sampler.h new file mode 100644 index 000000000..452ba924b --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/sampler.h @@ -0,0 +1,91 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/span_context.h" +#include "opentelemetry/trace/span_context_kv_iterable.h" +#include "opentelemetry/trace/trace_id.h" +#include "opentelemetry/trace/trace_state.h" +#include "opentelemetry/version.h" + +#include +#include +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +/** + * A sampling Decision for a Span to be created. + */ +enum class Decision +{ + // IsRecording() == false, span will not be recorded and all events and attributes will be + // dropped. + DROP, + // IsRecording() == true, but Sampled flag MUST NOT be set. + RECORD_ONLY, + // IsRecording() == true AND Sampled flag` MUST be set. + RECORD_AND_SAMPLE +}; + +/** + * The output of ShouldSample. + * It contains a sampling Decision and a set of Span Attributes. + */ +struct SamplingResult +{ + Decision decision; + // A set of span Attributes that will also be added to the Span. Can be nullptr. + std::unique_ptr> attributes; + // The tracestate used by the span. + nostd::shared_ptr trace_state; +}; + +/** + * The Sampler interface allows users to create custom samplers which will return a + * SamplingResult based on information that is typically available just before the Span was created. + */ +class Sampler +{ +public: + virtual ~Sampler() = default; + /** + * Called during Span creation to make a sampling decision. + * + * @param parent_context a const reference to the SpanContext of a parent Span. + * An invalid SpanContext if this is a root span. + * @param trace_id the TraceId for the new Span. This will be identical to that in + * the parentContext, unless this is a root span. + * @param name the name of the new Span. + * @param spanKind the opentelemetry::trace::SpanKind of the Span. + * @param attributes list of AttributeValue with their keys. + * @param links Collection of links that will be associated with the Span to be created. + * @return sampling result whether span should be sampled or not. + * @since 0.1.0 + */ + + virtual SamplingResult ShouldSample( + const opentelemetry::trace::SpanContext &parent_context, + opentelemetry::trace::TraceId trace_id, + nostd::string_view name, + opentelemetry::trace::SpanKind span_kind, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::trace::SpanContextKeyValueIterable &links) noexcept = 0; + + /** + * Returns the sampler name or short description with the configuration. + * This may be displayed on debug pages or in the logs. + * + * @return the description of this Sampler. + */ + virtual nostd::string_view GetDescription() const noexcept = 0; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/samplers/always_off.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/samplers/always_off.h new file mode 100644 index 000000000..2392b9b2f --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/samplers/always_off.h @@ -0,0 +1,48 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/sdk/trace/sampler.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +/** + * The always off sampler always returns DROP, effectively disabling + * tracing functionality. + */ +class AlwaysOffSampler : public Sampler +{ +public: + /** + * @return Returns DROP always + */ + SamplingResult ShouldSample( + const opentelemetry::trace::SpanContext &parent_context, + opentelemetry::trace::TraceId /*trace_id*/, + nostd::string_view /*name*/, + opentelemetry::trace::SpanKind /*span_kind*/, + const opentelemetry::common::KeyValueIterable & /*attributes*/, + const opentelemetry::trace::SpanContextKeyValueIterable & /*links*/) noexcept override + { + if (!parent_context.IsValid()) + { + return {Decision::DROP, nullptr, opentelemetry::trace::TraceState::GetDefault()}; + } + else + { + return {Decision::DROP, nullptr, parent_context.trace_state()}; + } + } + + /** + * @return Description MUST be AlwaysOffSampler + */ + nostd::string_view GetDescription() const noexcept override { return "AlwaysOffSampler"; } +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/samplers/always_on.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/samplers/always_on.h new file mode 100644 index 000000000..6c8583599 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/samplers/always_on.h @@ -0,0 +1,47 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/sdk/trace/sampler.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +/** + * The always on sampler is a default sampler which always return Decision::RECORD_AND_SAMPLE + */ +class AlwaysOnSampler : public Sampler +{ +public: + /** + * @return Always return Decision RECORD_AND_SAMPLE + */ + inline SamplingResult ShouldSample( + const opentelemetry::trace::SpanContext &parent_context, + opentelemetry::trace::TraceId /*trace_id*/, + nostd::string_view /*name*/, + opentelemetry::trace::SpanKind /*span_kind*/, + const opentelemetry::common::KeyValueIterable & /*attributes*/, + const opentelemetry::trace::SpanContextKeyValueIterable & /*links*/) noexcept override + { + if (!parent_context.IsValid()) + { + return {Decision::RECORD_AND_SAMPLE, nullptr, opentelemetry::trace::TraceState::GetDefault()}; + } + else + { + return {Decision::RECORD_AND_SAMPLE, nullptr, parent_context.trace_state()}; + } + } + + /** + * @return Description MUST be AlwaysOnSampler + */ + inline nostd::string_view GetDescription() const noexcept override { return "AlwaysOnSampler"; } +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/samplers/parent.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/samplers/parent.h new file mode 100644 index 000000000..c76a6c2a5 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/samplers/parent.h @@ -0,0 +1,45 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/sdk/common/atomic_shared_ptr.h" +#include "opentelemetry/sdk/trace/sampler.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +/** + * The ParentBased sampler is a composite sampler. ParentBased(delegateSampler) either respects + * the parent span's sampling decision or delegates to delegateSampler for root spans. + */ +class ParentBasedSampler : public Sampler +{ +public: + explicit ParentBasedSampler(std::shared_ptr delegate_sampler) noexcept; + /** The decision either respects the parent span's sampling decision or delegates to + * delegateSampler for root spans + * @return Returns DROP always + */ + SamplingResult ShouldSample( + const opentelemetry::trace::SpanContext &parent_context, + opentelemetry::trace::TraceId trace_id, + nostd::string_view name, + opentelemetry::trace::SpanKind span_kind, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::trace::SpanContextKeyValueIterable &links) noexcept override; + + /** + * @return Description MUST be ParentBased{delegate_sampler_.getDescription()} + */ + nostd::string_view GetDescription() const noexcept override; + +private: + const std::shared_ptr delegate_sampler_; + const std::string description_; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/samplers/trace_id_ratio.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/samplers/trace_id_ratio.h new file mode 100644 index 000000000..407b480d6 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/samplers/trace_id_ratio.h @@ -0,0 +1,53 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/sdk/trace/sampler.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +/** + * The TraceIdRatioBased sampler computes and returns a decision based on the + * provided trace_id and the configured ratio. + */ +class TraceIdRatioBasedSampler : public Sampler +{ +public: + /** + * @param ratio a required value, 1.0 >= ratio >= 0.0. If the given trace_id + * falls into a given ratio of all possible trace_id values, ShouldSample will + * return RECORD_AND_SAMPLE. + * @throws invalid_argument if ratio is out of bounds [0.0, 1.0] + */ + explicit TraceIdRatioBasedSampler(double ratio); + + /** + * @return Returns either RECORD_AND_SAMPLE or DROP based on current + * sampler configuration and provided trace_id and ratio. trace_id + * is used as a pseudorandom value in conjunction with the predefined + * ratio to determine whether this trace should be sampled + */ + SamplingResult ShouldSample( + const opentelemetry::trace::SpanContext & /*parent_context*/, + opentelemetry::trace::TraceId trace_id, + nostd::string_view /*name*/, + opentelemetry::trace::SpanKind /*span_kind*/, + const opentelemetry::common::KeyValueIterable & /*attributes*/, + const opentelemetry::trace::SpanContextKeyValueIterable & /*links*/) noexcept override; + + /** + * @return Description MUST be TraceIdRatioBasedSampler{0.000100} + */ + nostd::string_view GetDescription() const noexcept override; + +private: + std::string description_; + const uint64_t threshold_; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/simple_processor.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/simple_processor.h new file mode 100644 index 000000000..accc68596 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/simple_processor.h @@ -0,0 +1,84 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +#include "opentelemetry/common/spin_lock_mutex.h" +#include "opentelemetry/sdk/trace/exporter.h" +#include "opentelemetry/sdk/trace/processor.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +/** + * The simple span processor passes finished recordables to the configured + * SpanExporter, as soon as they are finished. + * + * OnEnd and ForceFlush are no-ops. + * + * All calls to the configured SpanExporter are synchronized using a + * spin-lock on an atomic_flag. + */ +class SimpleSpanProcessor : public SpanProcessor +{ +public: + /** + * Initialize a simple span processor. + * @param exporter the exporter used by the span processor + */ + explicit SimpleSpanProcessor(std::unique_ptr &&exporter) noexcept + : exporter_(std::move(exporter)) + {} + + std::unique_ptr MakeRecordable() noexcept override + { + return exporter_->MakeRecordable(); + } + + void OnStart(Recordable &span, + const opentelemetry::trace::SpanContext &parent_context) noexcept override + {} + + void OnEnd(std::unique_ptr &&span) noexcept override + { + nostd::span> batch(&span, 1); + const std::lock_guard locked(lock_); + if (exporter_->Export(batch) == sdk::common::ExportResult::kFailure) + { + /* Once it is defined how the SDK does logging, an error should be + * logged in this case. */ + } + } + + bool ForceFlush( + std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override + { + return true; + } + + bool Shutdown( + std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override + { + // We only call shutdown ONCE. + if (exporter_ != nullptr && !shutdown_latch_.test_and_set(std::memory_order_acquire)) + { + return exporter_->Shutdown(timeout); + } + return true; + } + + ~SimpleSpanProcessor() { Shutdown(); } + +private: + std::unique_ptr exporter_; + opentelemetry::common::SpinLockMutex lock_; + std::atomic_flag shutdown_latch_ = ATOMIC_FLAG_INIT; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/span_data.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/span_data.h new file mode 100644 index 000000000..0fb09f328 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/span_data.h @@ -0,0 +1,304 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include +#include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/common/timestamp.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/common/attribute_utils.h" +#include "opentelemetry/sdk/trace/recordable.h" +#include "opentelemetry/trace/canonical_code.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/span_id.h" +#include "opentelemetry/trace/trace_id.h" + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +/** + * Class for storing events in SpanData. + */ +class SpanDataEvent +{ +public: + SpanDataEvent(std::string name, + opentelemetry::common::SystemTimestamp timestamp, + const opentelemetry::common::KeyValueIterable &attributes) + : name_(name), timestamp_(timestamp), attribute_map_(attributes) + {} + + /** + * Get the name for this event + * @return the name for this event + */ + std::string GetName() const noexcept { return name_; } + + /** + * Get the timestamp for this event + * @return the timestamp for this event + */ + opentelemetry::common::SystemTimestamp GetTimestamp() const noexcept { return timestamp_; } + + /** + * Get the attributes for this event + * @return the attributes for this event + */ + const std::unordered_map &GetAttributes() const noexcept + { + return attribute_map_.GetAttributes(); + } + +private: + std::string name_; + opentelemetry::common::SystemTimestamp timestamp_; + common::AttributeMap attribute_map_; +}; + +/** + * Class for storing links in SpanData. + */ +class SpanDataLink +{ +public: + SpanDataLink(opentelemetry::trace::SpanContext span_context, + const opentelemetry::common::KeyValueIterable &attributes) + : span_context_(span_context), attribute_map_(attributes) + {} + + /** + * Get the attributes for this link + * @return the attributes for this link + */ + const std::unordered_map &GetAttributes() const noexcept + { + return attribute_map_.GetAttributes(); + } + + /** + * Get the span context for this link + * @return the span context for this link + */ + const opentelemetry::trace::SpanContext &GetSpanContext() const noexcept { return span_context_; } + +private: + opentelemetry::trace::SpanContext span_context_; + common::AttributeMap attribute_map_; +}; + +/** + * SpanData is a representation of all data collected by a span. + */ +class SpanData final : public Recordable +{ +public: + SpanData() : resource_{nullptr}, instrumentation_library_{nullptr} {} + /** + * Get the trace id for this span + * @return the trace id for this span + */ + opentelemetry::trace::TraceId GetTraceId() const noexcept { return span_context_.trace_id(); } + + /** + * Get the span id for this span + * @return the span id for this span + */ + opentelemetry::trace::SpanId GetSpanId() const noexcept { return span_context_.span_id(); } + + /** + * Get the span context for this span + * @return the span context for this span + */ + const opentelemetry::trace::SpanContext &GetSpanContext() const noexcept { return span_context_; } + + /** + * Get the parent span id for this span + * @return the span id for this span's parent + */ + opentelemetry::trace::SpanId GetParentSpanId() const noexcept { return parent_span_id_; } + + /** + * Get the name for this span + * @return the name for this span + */ + opentelemetry::nostd::string_view GetName() const noexcept { return name_; } + + /** + * Get the kind of this span + * @return the kind of this span + */ + opentelemetry::trace::SpanKind GetSpanKind() const noexcept { return span_kind_; } + + /** + * Get the status for this span + * @return the status for this span + */ + opentelemetry::trace::StatusCode GetStatus() const noexcept { return status_code_; } + + /** + * Get the status description for this span + * @return the description of the the status of this span + */ + opentelemetry::nostd::string_view GetDescription() const noexcept { return status_desc_; } + + /** + * Get the attributes associated with the resource + * @returns the attributes associated with the resource configured for TracerProvider + */ + + const opentelemetry::sdk::resource::Resource &GetResource() const noexcept + { + if (resource_ == nullptr) + { + // this shouldn't happen as TraceProvider provides default resources + static opentelemetry::sdk::resource::Resource resource = + opentelemetry::sdk::resource::Resource::GetEmpty(); + return resource; + } + return *resource_; + } + + /** + * Get the attributes associated with the resource + * @returns the attributes associated with the resource configured for TracerProvider + */ + + const opentelemetry::sdk::trace::InstrumentationLibrary &GetInstrumentationLibrary() + const noexcept + { + if (instrumentation_library_ == nullptr) + { + // this shouldn't happen as Tracer ensures there is valid default instrumentation library. + static std::unique_ptr + instrumentation_library = + opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary::Create( + "unknown_service"); + return *instrumentation_library; + } + return *instrumentation_library_; + } + + /** + * Get the start time for this span + * @return the start time for this span + */ + opentelemetry::common::SystemTimestamp GetStartTime() const noexcept { return start_time_; } + + /** + * Get the duration for this span + * @return the duration for this span + */ + std::chrono::nanoseconds GetDuration() const noexcept { return duration_; } + + /** + * Get the attributes for this span + * @return the attributes for this span + */ + const std::unordered_map &GetAttributes() const noexcept + { + return attribute_map_.GetAttributes(); + } + + /** + * Get the events associated with this span + * @return the events associated with this span + */ + const std::vector &GetEvents() const noexcept { return events_; } + + /** + * Get the links associated with this span + * @return the links associated with this span + */ + const std::vector &GetLinks() const noexcept { return links_; } + + void SetIdentity(const opentelemetry::trace::SpanContext &span_context, + opentelemetry::trace::SpanId parent_span_id) noexcept override + { + span_context_ = span_context; + parent_span_id_ = parent_span_id; + } + + void SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept override + { + attribute_map_.SetAttribute(key, value); + } + + void AddEvent(nostd::string_view name, + opentelemetry::common::SystemTimestamp timestamp = + opentelemetry::common::SystemTimestamp(std::chrono::system_clock::now()), + const opentelemetry::common::KeyValueIterable &attributes = + opentelemetry::common::KeyValueIterableView>( + {})) noexcept override + { + SpanDataEvent event(std::string(name), timestamp, attributes); + events_.push_back(event); + } + + void AddLink(const opentelemetry::trace::SpanContext &span_context, + const opentelemetry::common::KeyValueIterable &attributes) noexcept override + { + SpanDataLink link(span_context, attributes); + links_.push_back(link); + } + + void SetStatus(opentelemetry::trace::StatusCode code, + nostd::string_view description) noexcept override + { + status_code_ = code; + status_desc_ = std::string(description); + } + + void SetName(nostd::string_view name) noexcept override + { + name_ = std::string(name.data(), name.length()); + } + + void SetSpanKind(opentelemetry::trace::SpanKind span_kind) noexcept override + { + span_kind_ = span_kind; + } + + void SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept override + { + resource_ = &resource; + } + + void SetStartTime(opentelemetry::common::SystemTimestamp start_time) noexcept override + { + start_time_ = start_time; + } + + void SetDuration(std::chrono::nanoseconds duration) noexcept override { duration_ = duration; } + + void SetInstrumentationLibrary( + const InstrumentationLibrary &instrumentation_library) noexcept override + { + instrumentation_library_ = &instrumentation_library; + } + +private: + opentelemetry::trace::SpanContext span_context_{false, false}; + opentelemetry::trace::SpanId parent_span_id_; + opentelemetry::common::SystemTimestamp start_time_; + std::chrono::nanoseconds duration_{0}; + std::string name_; + opentelemetry::trace::StatusCode status_code_{opentelemetry::trace::StatusCode::kUnset}; + std::string status_desc_; + common::AttributeMap attribute_map_; + std::vector events_; + std::vector links_; + opentelemetry::trace::SpanKind span_kind_{opentelemetry::trace::SpanKind::kInternal}; + const opentelemetry::sdk::resource::Resource *resource_; + const InstrumentationLibrary *instrumentation_library_; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/tracer.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/tracer.h new file mode 100644 index 000000000..fd9f2c8e8 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/tracer.h @@ -0,0 +1,70 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/sdk/common/atomic_shared_ptr.h" +#include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h" +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/sdk/trace/processor.h" +#include "opentelemetry/sdk/trace/samplers/always_on.h" +#include "opentelemetry/sdk/trace/tracer_context.h" +#include "opentelemetry/trace/noop.h" +#include "opentelemetry/trace/tracer.h" +#include "opentelemetry/version.h" + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ + +using namespace opentelemetry::sdk::instrumentationlibrary; + +class Tracer final : public trace_api::Tracer, public std::enable_shared_from_this +{ +public: + /** Construct a new Tracer with the given context pipeline. */ + explicit Tracer(std::shared_ptr context, + std::unique_ptr instrumentation_library = + InstrumentationLibrary::Create("")) noexcept; + + nostd::shared_ptr StartSpan( + nostd::string_view name, + const opentelemetry::common::KeyValueIterable &attributes, + const trace_api::SpanContextKeyValueIterable &links, + const trace_api::StartSpanOptions &options = {}) noexcept override; + + void ForceFlushWithMicroseconds(uint64_t timeout) noexcept override; + + void CloseWithMicroseconds(uint64_t timeout) noexcept override; + + /** Returns the configured span processor. */ + SpanProcessor &GetProcessor() noexcept { return context_->GetProcessor(); } + + /** Returns the configured Id generator */ + IdGenerator &GetIdGenerator() const noexcept { return context_->GetIdGenerator(); } + + /** Returns the associated instruementation library */ + const InstrumentationLibrary &GetInstrumentationLibrary() const noexcept + { + return *instrumentation_library_; + } + + /** Returns the currently configured resource **/ + const opentelemetry::sdk::resource::Resource &GetResource() { return context_->GetResource(); } + + // Note: Test only + Sampler &GetSampler() { return context_->GetSampler(); } + +private: + // order of declaration is important here - instrumentation library should destroy after + // tracer-context. + std::shared_ptr instrumentation_library_; + std::shared_ptr context_; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/tracer_context.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/tracer_context.h new file mode 100644 index 000000000..572f60caf --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/tracer_context.h @@ -0,0 +1,100 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/sdk/common/atomic_unique_ptr.h" +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/sdk/trace/processor.h" +#include "opentelemetry/sdk/trace/random_id_generator.h" +#include "opentelemetry/sdk/trace/samplers/always_on.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ + +/** + * A class which stores the TracerProvider context. + * + * This class meets the following design criteria: + * - A shared reference between TracerProvider and Tracers instantiated. + * - A thread-safe class that allows updating/altering processor/exporter pipelines + * and sampling config. + * - The owner/destroyer of Processors/Exporters. These will remain active until + * this class is destroyed. I.e. Sampling, Exporting, flushing, Custom Iterator etc. are all ok + * if this object is alive, and they will work together. If this object is destroyed, then no shared + * references to Processor, Exporter, Recordable, Custom Iterator etc. should exist, and all + * associated pipelines will have been flushed. + */ +class TracerContext +{ +public: + explicit TracerContext( + std::vector> &&processor, + opentelemetry::sdk::resource::Resource resource = + opentelemetry::sdk::resource::Resource::Create({}), + std::unique_ptr sampler = std::unique_ptr(new AlwaysOnSampler), + std::unique_ptr id_generator = + std::unique_ptr(new RandomIdGenerator())) noexcept; + + /** + * Attaches a span processor to list of configured processors to this tracer context. + * Processor once attached can't be removed. + * @param processor The new span processor for this tracer. This must not be + * a nullptr. Ownership is given to the `TracerContext`. + * + * Note: This method is not thread safe. + */ + void AddProcessor(std::unique_ptr processor) noexcept; + + /** + * Obtain the sampler associated with this tracer. + * @return The sampler for this tracer. + */ + Sampler &GetSampler() const noexcept; + + /** + * Obtain the configured (composite) processor. + * + * Note: When more than one processor is active, this will + * return an "aggregate" processor + */ + SpanProcessor &GetProcessor() const noexcept; + + /** + * Obtain the resource associated with this tracer context. + * @return The resource for this tracer context. + */ + const opentelemetry::sdk::resource::Resource &GetResource() const noexcept; + + /** + * Obtain the Id Generator associated with this tracer context. + * @return The ID Generator for this tracer context. + */ + opentelemetry::sdk::trace::IdGenerator &GetIdGenerator() const noexcept; + + /** + * Force all active SpanProcessors to flush any buffered spans + * within the given timeout. + */ + bool ForceFlush(std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept; + + /** + * Shutdown the span processor associated with this tracer provider. + */ + bool Shutdown() noexcept; + +private: + // order of declaration is important here - resource object should be destroyed after processor. + opentelemetry::sdk::resource::Resource resource_; + std::unique_ptr sampler_; + std::unique_ptr id_generator_; + std::unique_ptr processor_; +}; + +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/tracer_provider.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/tracer_provider.h new file mode 100644 index 000000000..aa0f69a2b --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/trace/tracer_provider.h @@ -0,0 +1,103 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include +#include +#include + +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/sdk/trace/processor.h" +#include "opentelemetry/sdk/trace/samplers/always_on.h" +#include "opentelemetry/sdk/trace/tracer.h" +#include "opentelemetry/sdk/trace/tracer_context.h" +#include "opentelemetry/trace/tracer_provider.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +class TracerProvider final : public opentelemetry::trace::TracerProvider +{ +public: + /** + * Initialize a new tracer provider with a specified sampler + * @param processor The span processor for this tracer provider. This must + * not be a nullptr. + * @param resource The resources for this tracer provider. + * @param sampler The sampler for this tracer provider. This must + * not be a nullptr. + * @param id_generator The custom id generator for this tracer provider. This must + * not be a nullptr + */ + explicit TracerProvider( + std::unique_ptr processor, + opentelemetry::sdk::resource::Resource resource = + opentelemetry::sdk::resource::Resource::Create({}), + std::unique_ptr sampler = std::unique_ptr(new AlwaysOnSampler), + std::unique_ptr id_generator = + std::unique_ptr( + new RandomIdGenerator())) noexcept; + + explicit TracerProvider( + std::vector> &&processors, + opentelemetry::sdk::resource::Resource resource = + opentelemetry::sdk::resource::Resource::Create({}), + std::unique_ptr sampler = std::unique_ptr(new AlwaysOnSampler), + std::unique_ptr id_generator = + std::unique_ptr( + new RandomIdGenerator())) noexcept; + + /** + * Initialize a new tracer provider with a specified context + * @param context The shared tracer configuration/pipeline for this provider. + */ + explicit TracerProvider(std::shared_ptr context) noexcept; + + ~TracerProvider(); + + opentelemetry::nostd::shared_ptr GetTracer( + nostd::string_view library_name, + nostd::string_view library_version = "", + nostd::string_view schema_url = "") noexcept override; + + /** + * Attaches a span processor to list of configured processors for this tracer provider. + * @param processor The new span processor for this tracer provider. This + * must not be a nullptr. + * + * Note: This process may not receive any in-flight spans, but will get newly created spans. + * Note: This method is not thread safe, and should ideally be called from main thread. + */ + void AddProcessor(std::unique_ptr processor) noexcept; + + /** + * Obtain the resource associated with this tracer provider. + * @return The resource for this tracer provider. + */ + const opentelemetry::sdk::resource::Resource &GetResource() const noexcept; + + /** + * Shutdown the span processor associated with this tracer provider. + */ + bool Shutdown() noexcept; + + /** + * Force flush the span processor associated with this tracer provider. + */ + bool ForceFlush(std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept; + +private: + // order of declaration is important here - tracers should destroy only after context. + std::vector> tracers_; + std::shared_ptr context_; + std::mutex lock_; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/version/version.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/version/version.h new file mode 100644 index 000000000..65c3c826c --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk/version/version.h @@ -0,0 +1,30 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/detail/preprocessor.h" + +#define OPENTELEMETRY_SDK_VERSION "1.4.0" + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace version +{ +extern const int MAJOR_VERSION; +extern const int MINOR_VERSION; +extern const int PATCH_VERSION; +extern const char *PRE_RELEASE; +extern const char *BUILD_METADATA; +extern const int COUNT_NEW_COMMITS; +extern const char *BRANCH; +extern const char *COMMIT_HASH; +extern const char *FULL_VERSION; +extern const char *FULL_VERSION_WITH_BRANCH_COMMITHASH; +extern const char *BUILD_DATE; +} // namespace version +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk_config.h b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk_config.h new file mode 100644 index 000000000..8ac72a10d --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/include/opentelemetry/sdk_config.h @@ -0,0 +1,7 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/config.h" +#include "opentelemetry/sdk/common/global_log_handler.h" \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/src/CMakeLists.txt new file mode 100644 index 000000000..aab294b6d --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/CMakeLists.txt @@ -0,0 +1,12 @@ +add_subdirectory(common) +add_subdirectory(trace) +if(WITH_METRICS_PREVIEW) + add_subdirectory(_metrics) +else() + add_subdirectory(metrics) +endif() +if(WITH_LOGS_PREVIEW) + add_subdirectory(logs) +endif() +add_subdirectory(version) +add_subdirectory(resource) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/BUILD new file mode 100644 index 000000000..4b528dda7 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/BUILD @@ -0,0 +1,26 @@ +# Copyright 2020, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "metrics_deprecated", + srcs = glob(["**/*.cc"]), + hdrs = glob(["**/*.h"]), + include_prefix = "src/_metrics", + deps = [ + "//api", + "//sdk:headers", + ], +) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/CMakeLists.txt new file mode 100644 index 000000000..c1717ce89 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/CMakeLists.txt @@ -0,0 +1,19 @@ +add_library(opentelemetry_metrics_deprecated meter_provider.cc meter.cc + ungrouped_processor.cc) + +set_target_properties(opentelemetry_metrics_deprecated + PROPERTIES EXPORT_NAME metrics_deprecated) + +target_link_libraries(opentelemetry_metrics_deprecated + PUBLIC opentelemetry_common) + +target_include_directories( + opentelemetry_metrics_deprecated + PUBLIC "$") + +install( + TARGETS opentelemetry_metrics_deprecated + EXPORT "${PROJECT_NAME}-target" + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/meter.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/meter.cc new file mode 100644 index 000000000..f48eccfbd --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/meter.cc @@ -0,0 +1,782 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_METRICS_PREVIEW + +# include "opentelemetry/sdk/_metrics/meter.h" +# include "opentelemetry/common/macros.h" + +namespace metrics_api = opentelemetry::metrics; + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +nostd::shared_ptr> Meter::NewShortCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto counter = new Counter(name, description, unit, enabled); + auto ptr = std::shared_ptr>(counter); + metrics_lock_.lock(); + short_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewIntCounter(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto counter = new Counter(name, description, unit, enabled); + auto ptr = std::shared_ptr>(counter); + metrics_lock_.lock(); + int_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewFloatCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto counter = new Counter(name, description, unit, enabled); + auto ptr = std::shared_ptr>(counter); + metrics_lock_.lock(); + float_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewDoubleCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto counter = new Counter(name, description, unit, enabled); + auto ptr = std::shared_ptr>(counter); + metrics_lock_.lock(); + double_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewShortUpDownCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto udcounter = new UpDownCounter(name, description, unit, enabled); + auto ptr = std::shared_ptr>(udcounter); + metrics_lock_.lock(); + short_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewIntUpDownCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto udcounter = new UpDownCounter(name, description, unit, enabled); + auto ptr = std::shared_ptr>(udcounter); + metrics_lock_.lock(); + int_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewFloatUpDownCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto udcounter = new UpDownCounter(name, description, unit, enabled); + auto ptr = std::shared_ptr>(udcounter); + metrics_lock_.lock(); + float_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewDoubleUpDownCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto udcounter = new UpDownCounter(name, description, unit, enabled); + auto ptr = std::shared_ptr>(udcounter); + metrics_lock_.lock(); + double_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewShortValueRecorder( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto recorder = new ValueRecorder(name, description, unit, enabled); + auto ptr = std::shared_ptr>(recorder); + metrics_lock_.lock(); + short_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewIntValueRecorder( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto recorder = new ValueRecorder(name, description, unit, enabled); + auto ptr = std::shared_ptr>(recorder); + metrics_lock_.lock(); + int_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewFloatValueRecorder( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto recorder = new ValueRecorder(name, description, unit, enabled); + auto ptr = std::shared_ptr>(recorder); + metrics_lock_.lock(); + float_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewDoubleValueRecorder( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto recorder = new ValueRecorder(name, description, unit, enabled); + auto ptr = std::shared_ptr>(recorder); + metrics_lock_.lock(); + double_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewShortSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto sumobs = new SumObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + short_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewIntSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto sumobs = new SumObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + int_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewFloatSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto sumobs = new SumObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + float_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewDoubleSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto sumobs = new SumObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + double_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewShortUpDownSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto sumobs = new UpDownSumObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + short_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewIntUpDownSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto sumobs = new UpDownSumObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + int_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewFloatUpDownSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto sumobs = new UpDownSumObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + float_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewDoubleUpDownSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto sumobs = new UpDownSumObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + double_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewShortValueObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto sumobs = new ValueObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + short_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewIntValueObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto sumobs = new ValueObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + int_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewFloatValueObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto sumobs = new ValueObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + float_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewDoubleValueObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +# if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +# else + std::terminate(); +# endif + } + auto sumobs = new ValueObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + double_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +void Meter::RecordShortBatch(const common::KeyValueIterable &labels, + nostd::span *> instruments, + nostd::span values) noexcept +{ + for (size_t i = 0; i < instruments.size(); ++i) + { + instruments[i]->update(values[i], labels); + } +} + +void Meter::RecordIntBatch(const common::KeyValueIterable &labels, + nostd::span *> instruments, + nostd::span values) noexcept +{ + for (size_t i = 0; i < instruments.size(); ++i) + { + instruments[i]->update(values[i], labels); + } +} + +void Meter::RecordFloatBatch(const common::KeyValueIterable &labels, + nostd::span *> instruments, + nostd::span values) noexcept +{ + for (size_t i = 0; i < instruments.size(); ++i) + { + instruments[i]->update(values[i], labels); + } +} + +void Meter::RecordDoubleBatch(const common::KeyValueIterable &labels, + nostd::span *> instruments, + nostd::span values) noexcept +{ + for (size_t i = 0; i < instruments.size(); ++i) + { + instruments[i]->update(values[i], labels); + } +} + +std::vector Meter::Collect() noexcept +{ + std::vector records; + CollectMetrics(records); + CollectObservers(records); + return records; +} + +// Must cast to sdk::SynchronousInstrument to have access to GetRecords() function +void Meter::CollectMetrics(std::vector &records) +{ + metrics_lock_.lock(); + for (auto i = short_metrics_.begin(); i != short_metrics_.end();) + { + CollectSingleSyncInstrument(i, records); + if (i->second.use_count() == 1) // Evaluates to true if user's shared_ptr has been deleted + { + i = short_metrics_.erase(i); // Remove instrument that is no longer accessible + } + else + { + i++; + } + } + for (auto i = int_metrics_.begin(); i != int_metrics_.end();) + { + CollectSingleSyncInstrument(i, records); + if (i->second.use_count() == 1) // Evaluates to true if user's shared_ptr has been deleted + { + i = int_metrics_.erase(i); // Remove instrument that is no longer accessible + } + else + { + i++; + } + } + for (auto i = float_metrics_.begin(); i != float_metrics_.end();) + { + CollectSingleSyncInstrument(i, records); + if (i->second.use_count() == 1) // Evaluates to true if user's shared_ptr has been deleted + { + i = float_metrics_.erase(i); // Remove instrument that is no longer accessible + } + else + { + i++; + } + } + for (auto i = double_metrics_.begin(); i != double_metrics_.end();) + { + CollectSingleSyncInstrument(i, records); + if (i->second.use_count() == 1) // Evaluates to true if user's shared_ptr has been deleted + { + i = double_metrics_.erase(i); // Remove instrument that is no longer accessible + } + else + { + i++; + } + } + metrics_lock_.unlock(); +} + +template +void Meter::CollectSingleSyncInstrument( + typename std::map>>::iterator + i, + std::vector &records) +{ + if (!i->second->IsEnabled()) + { + i++; + return; + } +# ifdef OPENTELEMETRY_RTTI_ENABLED + auto cast_ptr = std::dynamic_pointer_cast>(i->second); +# else + auto cast_ptr = std::static_pointer_cast>(i->second); +# endif + std::vector new_records = cast_ptr->GetRecords(); + records.insert(records.begin(), new_records.begin(), new_records.end()); +} + +void Meter::CollectObservers(std::vector &records) +{ + observers_lock_.lock(); + for (auto i = short_observers_.begin(); i != short_observers_.end();) + { + CollectSingleAsyncInstrument(i, records); + if (i->second.use_count() == 1) + { + i = short_observers_.erase(i); + } + else + { + i++; + } + } + for (auto i = int_observers_.begin(); i != int_observers_.end();) + { + CollectSingleAsyncInstrument(i, records); + if (i->second.use_count() == 1) + { + i = int_observers_.erase(i); + } + else + { + i++; + } + } + for (auto i = float_observers_.begin(); i != float_observers_.end();) + { + CollectSingleAsyncInstrument(i, records); + if (i->second.use_count() == 1) + { + i = float_observers_.erase(i); + } + else + { + i++; + } + } + for (auto i = double_observers_.begin(); i != double_observers_.end();) + { + CollectSingleAsyncInstrument(i, records); + if (i->second.use_count() == 1) + { + i = double_observers_.erase(i); + } + else + { + i++; + } + } + observers_lock_.unlock(); +} + +template +void Meter::CollectSingleAsyncInstrument( + typename std::map>>::iterator i, + std::vector &records) +{ + if (!i->second->IsEnabled()) + { + i++; + return; + } +# ifdef OPENTELEMETRY_RTTI_ENABLED + auto cast_ptr = std::dynamic_pointer_cast>(i->second); +# else + auto cast_ptr = std::static_pointer_cast>(i->second); +# endif + std::vector new_records = cast_ptr->GetRecords(); + records.insert(records.begin(), new_records.begin(), new_records.end()); +} + +bool Meter::IsValidName(nostd::string_view name) +{ + if (name.empty() || isdigit(name[0]) || isspace(name[0]) || ispunct(name[0])) + return false; + else + { + for (size_t i = 0; i < name.size(); ++i) + { + if (!isalnum(name[i]) && name[i] != '_' && name[i] != '.' && name[i] != '-') + return false; + } + } + return true; +} + +bool Meter::NameAlreadyUsed(nostd::string_view name) +{ + std::lock_guard lg_metrics(metrics_lock_); + std::lock_guard lg_obsevers(observers_lock_); + if (names_.find(std::string(name)) != names_.end()) + return true; + else + { + names_.insert(std::string(name)); + return false; + } +} +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/meter_provider.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/meter_provider.cc new file mode 100644 index 000000000..9a36817a6 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/meter_provider.cc @@ -0,0 +1,29 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/_metrics/meter_provider.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +namespace nostd = opentelemetry::nostd; +namespace metrics_api = opentelemetry::metrics; + +MeterProvider::MeterProvider(std::string library_name, std::string library_version) noexcept + : meter_(new Meter(library_name, library_version)) +{} + +nostd::shared_ptr MeterProvider::GetMeter( + nostd::string_view library_name, + nostd::string_view library_version) noexcept +{ + return nostd::shared_ptr(meter_); +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/ungrouped_processor.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/ungrouped_processor.cc new file mode 100644 index 000000000..b3d1a94eb --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/_metrics/ungrouped_processor.cc @@ -0,0 +1,178 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/_metrics/ungrouped_processor.h" + +OPENTELEMETRY_BEGIN_NAMESPACE + +namespace sdk +{ + +namespace metrics +{ + +UngroupedMetricsProcessor::UngroupedMetricsProcessor(bool stateful) +{ + stateful_ = stateful; +} + +/** + * CheckpointSelf will return a vector of records to be exported. This function will go through + *aggregators that have been sent through process() and return them as a vector of records. + **/ +std::vector +UngroupedMetricsProcessor::CheckpointSelf() noexcept +{ + std::vector metric_records; + + for (auto iter : batch_map_) + { + // Create a record from the held KeyStruct values and add to the Checkpoint + KeyStruct key = iter.first; + opentelemetry::sdk::metrics::Record r{key.name, key.description, key.labels, iter.second}; + + metric_records.push_back(r); + } + + return metric_records; +} + +/** + * Once Process is called, FinishCollection() should also be called. In the case of a non stateful + *processor the map will be reset. + **/ +void UngroupedMetricsProcessor::FinishedCollection() noexcept +{ + if (!stateful_) + { + batch_map_ = {}; + } +} + +void UngroupedMetricsProcessor::process(opentelemetry::sdk::metrics::Record record) noexcept +{ + auto aggregator = record.GetAggregator(); + std::string label = record.GetLabels(); + std::string name = record.GetName(); + std::string description = record.GetDescription(); + + KeyStruct batch_key = KeyStruct(name, description, label, get_instrument(aggregator)); + + /** + * If we have already seen this aggregator then we will merge it with the copy that exists in the + *batch_map_ The call to merge here combines only identical records (same key) + **/ + if (batch_map_.find(batch_key) != batch_map_.end()) + { + auto batch_value = batch_map_[batch_key]; + + if (nostd::holds_alternative>>( + aggregator)) + { + auto batch_value_reference_short = + nostd::get>>(batch_value); + auto aggregator_reference_short = + nostd::get>>(aggregator); + + merge_aggregators(batch_value_reference_short, aggregator_reference_short); + } + else if (nostd::holds_alternative< + std::shared_ptr>>(aggregator)) + { + auto batch_value_reference_int = + nostd::get>>(batch_value); + auto aggregator_reference_int = + nostd::get>>(aggregator); + + merge_aggregators(batch_value_reference_int, aggregator_reference_int); + } + else if (nostd::holds_alternative< + std::shared_ptr>>(aggregator)) + { + auto batch_value_reference_float = + nostd::get>>(batch_value); + auto aggregator_reference_float = + nostd::get>>(aggregator); + + merge_aggregators(batch_value_reference_float, aggregator_reference_float); + } + else if (nostd::holds_alternative< + std::shared_ptr>>(aggregator)) + { + auto batch_value_reference_double = + nostd::get>>(batch_value); + auto aggregator_reference_double = + nostd::get>>(aggregator); + + merge_aggregators(batch_value_reference_double, aggregator_reference_double); + } + return; + } + /** + * If the processor is stateful and this aggregator has not be seen by the processor yet. + * We create a copy of this aggregator, merge it with the aggregator from the given record. + * Then set this copied aggregator with the batch_key in the batch_map_ + **/ + if (stateful_) + { + if (nostd::holds_alternative>>( + aggregator)) + { + auto record_agg_short = + nostd::get>>(aggregator); + auto aggregator_short = aggregator_copy(record_agg_short); + + merge_aggregators(aggregator_short, record_agg_short); + + batch_map_[batch_key] = aggregator_short; + } + else if (nostd::holds_alternative< + std::shared_ptr>>(aggregator)) + { + auto record_agg_int = + nostd::get>>(aggregator); + auto aggregator_int = aggregator_copy(record_agg_int); + + merge_aggregators(aggregator_int, record_agg_int); + + batch_map_[batch_key] = aggregator_int; + } + else if (nostd::holds_alternative< + std::shared_ptr>>(aggregator)) + { + auto record_agg_float = + nostd::get>>(aggregator); + auto aggregator_float = aggregator_copy(record_agg_float); + + merge_aggregators(aggregator_float, record_agg_float); + + batch_map_[batch_key] = aggregator_float; + } + else if (nostd::holds_alternative< + std::shared_ptr>>(aggregator)) + { + auto record_agg_double = + nostd::get>>(aggregator); + auto aggregator_double = aggregator_copy(record_agg_double); + + merge_aggregators(aggregator_double, record_agg_double); + + batch_map_[batch_key] = aggregator_double; + } + } + else + { + /** + * If the processor is not stateful, we don't need to create a copy of the aggregator, since the + *map will be reset from FinishedCollection(). + **/ + batch_map_[batch_key] = aggregator; + } +} + +} // namespace metrics +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/common/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/BUILD new file mode 100644 index 000000000..b8369fc6f --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/BUILD @@ -0,0 +1,44 @@ +# Copyright 2020, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "random", + srcs = [ + "core.cc", + "random.cc", + ], + hdrs = [ + "fast_random_number_generator.h", + "random.h", + ], + include_prefix = "src/common", + deps = [ + "//api", + "//sdk:headers", + "//sdk/src/common/platform:fork", + ], +) + +cc_library( + name = "global_log_handler", + srcs = [ + "global_log_handler.cc", + ], + deps = [ + "//api", + "//sdk:headers", + ], +) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/common/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/CMakeLists.txt new file mode 100644 index 000000000..d8674353b --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/CMakeLists.txt @@ -0,0 +1,21 @@ +set(COMMON_SRCS random.cc core.cc global_log_handler.cc) +if(WIN32) + list(APPEND COMMON_SRCS platform/fork_windows.cc) +else() + list(APPEND COMMON_SRCS platform/fork_unix.cc) +endif() + +add_library(opentelemetry_common ${COMMON_SRCS}) + +set_target_properties(opentelemetry_common PROPERTIES EXPORT_NAME common) + +target_link_libraries( + opentelemetry_common PUBLIC opentelemetry_api opentelemetry_sdk + Threads::Threads) + +install( + TARGETS opentelemetry_common + EXPORT "${PROJECT_NAME}-target" + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/common/core.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/core.cc new file mode 100644 index 000000000..16012e765 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/core.cc @@ -0,0 +1,8 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// clang-format off +// version.h should be included before nostd/variant.h. +#include "opentelemetry/version.h" +#include "opentelemetry/nostd/variant.h" +// clang-format on diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/common/fast_random_number_generator.h b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/fast_random_number_generator.h new file mode 100644 index 000000000..9887f2f3b --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/fast_random_number_generator.h @@ -0,0 +1,82 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ +/** + * Profiling shows that random number generation can be a significant cost of + * span generation. This provides a faster random number generator than + * std::mt19937_64; and since we don't care about the other beneficial random + * number properties that std:mt19937_64 provides for this application, it's a + * entirely appropriate replacement. + * + * Note for Windows users - please make sure that NOMINMAX is defined, e.g. + * + * ... + * #define NOMINMAX + * #include + * ... + * + * See: + * https://stackoverflow.com/questions/13416418/define-nominmax-using-stdmin-max + * + */ +class FastRandomNumberGenerator +{ +public: + using result_type = uint64_t; + + FastRandomNumberGenerator() noexcept = default; + + template + FastRandomNumberGenerator(SeedSequence &seed_sequence) noexcept + { + seed(seed_sequence); + } + + uint64_t operator()() noexcept + { + // Uses the xorshift128p random number generation algorithm described in + // https://en.wikipedia.org/wiki/Xorshift + auto &state_a = state_[0]; + auto &state_b = state_[1]; + auto t = state_a; + auto s = state_b; + state_a = s; + t ^= t << 23; // a + t ^= t >> 17; // b + t ^= s ^ (s >> 26); // c + state_b = t; + return t + s; + } + + // RandomNumberGenerator concept functions required from standard library. + // See http://www.cplusplus.com/reference/random/mt19937/ + template + void seed(SeedSequence &seed_sequence) noexcept + { + seed_sequence.generate(reinterpret_cast(state_.data()), + reinterpret_cast(state_.data() + state_.size())); + } + + static constexpr uint64_t min() noexcept { return 0; } + + static constexpr uint64_t max() noexcept { return std::numeric_limits::max(); } + +private: + std::array state_{}; +}; +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/common/global_log_handler.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/global_log_handler.cc new file mode 100644 index 000000000..c86b652c7 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/global_log_handler.cc @@ -0,0 +1,57 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/common/global_log_handler.h" + +#include +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ +namespace internal_log +{ + +LogHandler::~LogHandler() {} + +void DefaultLogHandler::Handle(LogLevel level, + const char *file, + int line, + const char *msg, + const sdk::common::AttributeMap &attributes) noexcept +{ + std::stringstream output_s; + output_s << "[" << LevelToString(level) << "] "; + if (file != nullptr) + { + output_s << "File: " << file << ":" << line; + } + if (msg != nullptr) + { + output_s << msg; + } + output_s << std::endl; + // TBD - print attributes + std::cout << output_s.str(); // thread safe. +} + +void NoopLogHandler::Handle(LogLevel, + const char *, + int, + const char *, + const sdk::common::AttributeMap &) noexcept +{} + +std::pair, LogLevel> &GlobalLogHandler::GetHandlerAndLevel() noexcept +{ + static std::pair, LogLevel> handler_and_level{ + nostd::shared_ptr(new DefaultLogHandler), LogLevel::Warning}; + return handler_and_level; +} + +} // namespace internal_log +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/common/platform/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/platform/BUILD new file mode 100644 index 000000000..c96a1417a --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/platform/BUILD @@ -0,0 +1,34 @@ +# Copyright 2020, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "fork", + srcs = select({ + "//bazel:windows": ["fork_windows.cc"], + "//conditions:default": ["fork_unix.cc"], + }), + hdrs = [ + "fork.h", + ], + include_prefix = "src/common/platform", + linkopts = select({ + "//bazel:windows": [], + "//conditions:default": ["-pthread"], + }), + deps = [ + "//api", + ], +) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/common/platform/fork.h b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/platform/fork.h new file mode 100644 index 000000000..b1a0e9d77 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/platform/fork.h @@ -0,0 +1,24 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ +namespace platform +{ +/** + * Portable wrapper for pthread_atfork. + * See + * https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_atfork.html + */ +int AtFork(void (*prepare)(), void (*parent)(), void (*child)()) noexcept; +} // namespace platform +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/common/platform/fork_unix.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/platform/fork_unix.cc new file mode 100644 index 000000000..cfbd8ab77 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/platform/fork_unix.cc @@ -0,0 +1,22 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "src/common/platform/fork.h" + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ +namespace platform +{ +int AtFork(void (*prepare)(), void (*parent)(), void (*child)()) noexcept +{ + return ::pthread_atfork(prepare, parent, child); +} +} // namespace platform +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/common/platform/fork_windows.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/platform/fork_windows.cc new file mode 100644 index 000000000..9d9f2bf79 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/platform/fork_windows.cc @@ -0,0 +1,23 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "src/common/platform/fork.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ +namespace platform +{ +int AtFork(void (*prepare)(), void (*parent)(), void (*child)()) noexcept +{ + (void)prepare; + (void)parent; + (void)child; + return 0; +} +} // namespace platform +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/common/random.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/random.cc new file mode 100644 index 000000000..77b88cfa2 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/random.cc @@ -0,0 +1,80 @@ + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "src/common/random.h" +#include "src/common/platform/fork.h" + +#include +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ +// Wraps a thread_local random number generator, but adds a fork handler so that +// the generator will be correctly seeded after forking. +// +// See https://stackoverflow.com/q/51882689/4447365 and +// https://github.com/opentracing-contrib/nginx-opentracing/issues/52 +namespace +{ +class TlsRandomNumberGenerator +{ +public: + TlsRandomNumberGenerator() noexcept + { + Seed(); + platform::AtFork(nullptr, nullptr, OnFork); + } + + static FastRandomNumberGenerator &engine() noexcept { return engine_; } + +private: + static thread_local FastRandomNumberGenerator engine_; + + static void OnFork() noexcept { Seed(); } + + static void Seed() noexcept + { + std::random_device random_device; + std::seed_seq seed_seq{random_device(), random_device(), random_device(), random_device()}; + engine_.seed(seed_seq); + } +}; + +thread_local FastRandomNumberGenerator TlsRandomNumberGenerator::engine_{}; +} // namespace + +FastRandomNumberGenerator &Random::GetRandomNumberGenerator() noexcept +{ + static thread_local TlsRandomNumberGenerator random_number_generator{}; + return TlsRandomNumberGenerator::engine(); +} + +uint64_t Random::GenerateRandom64() noexcept +{ + return GetRandomNumberGenerator()(); +} + +void Random::GenerateRandomBuffer(opentelemetry::nostd::span buffer) noexcept +{ + auto buf_size = buffer.size(); + + for (size_t i = 0; i < buf_size; i += sizeof(uint64_t)) + { + uint64_t value = GenerateRandom64(); + if (i + sizeof(uint64_t) <= buf_size) + { + memcpy(&buffer[i], &value, sizeof(uint64_t)); + } + else + { + memcpy(&buffer[i], &value, buf_size - i); + } + } +} +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/common/random.h b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/random.h new file mode 100644 index 000000000..ecd6dabc1 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/common/random.h @@ -0,0 +1,41 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/version.h" +#include "src/common/fast_random_number_generator.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ +/** + * Utility methods for creating random data, based on a seeded thread-local + * number generator. + */ +class Random +{ +public: + /** + * @return an unsigned 64 bit random number + */ + static uint64_t GenerateRandom64() noexcept; + /** + * Fill the passed span with random bytes. + * + * @param buffer A span of bytes. + */ + static void GenerateRandomBuffer(opentelemetry::nostd::span buffer) noexcept; + +private: + /** + * @return a seeded thread-local random number generator. + */ + static FastRandomNumberGenerator &GetRandomNumberGenerator() noexcept; +}; +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/BUILD new file mode 100644 index 000000000..53465cb8b --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/BUILD @@ -0,0 +1,28 @@ +# Copyright 2020, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "logs", + srcs = glob(["**/*.cc"]), + hdrs = glob(["**/*.h"]), + include_prefix = "src/logs", + deps = [ + "//api", + "//sdk:headers", + "//sdk/src/common:global_log_handler", + "//sdk/src/resource", + ], +) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/CMakeLists.txt new file mode 100644 index 000000000..20f13324e --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/CMakeLists.txt @@ -0,0 +1,25 @@ +add_library( + opentelemetry_logs + logger_provider.cc + logger.cc + simple_log_processor.cc + batch_log_processor.cc + logger_context.cc + multi_log_processor.cc + multi_recordable.cc) + +set_target_properties(opentelemetry_logs PROPERTIES EXPORT_NAME logs) + +target_link_libraries(opentelemetry_logs PUBLIC opentelemetry_resources + opentelemetry_common) + +target_include_directories( + opentelemetry_logs + PUBLIC "$") + +install( + TARGETS opentelemetry_logs + EXPORT "${PROJECT_NAME}-target" + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/batch_log_processor.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/batch_log_processor.cc new file mode 100644 index 000000000..9b20705b0 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/batch_log_processor.cc @@ -0,0 +1,208 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW +# include "opentelemetry/sdk/logs/batch_log_processor.h" + +# include +using opentelemetry::sdk::common::AtomicUniquePtr; +using opentelemetry::sdk::common::CircularBufferRange; + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ +BatchLogProcessor::BatchLogProcessor(std::unique_ptr &&exporter, + const size_t max_queue_size, + const std::chrono::milliseconds scheduled_delay_millis, + const size_t max_export_batch_size) + : exporter_(std::move(exporter)), + max_queue_size_(max_queue_size), + scheduled_delay_millis_(scheduled_delay_millis), + max_export_batch_size_(max_export_batch_size), + buffer_(max_queue_size_), + worker_thread_(&BatchLogProcessor::DoBackgroundWork, this) +{} + +std::unique_ptr BatchLogProcessor::MakeRecordable() noexcept +{ + return exporter_->MakeRecordable(); +} + +void BatchLogProcessor::OnReceive(std::unique_ptr &&record) noexcept +{ + if (is_shutdown_.load() == true) + { + return; + } + + if (buffer_.Add(record) == false) + { + return; + } + + // If the queue gets at least half full a preemptive notification is + // sent to the worker thread to start a new export cycle. + if (buffer_.size() >= max_queue_size_ / 2) + { + // signal the worker thread + cv_.notify_one(); + } +} + +bool BatchLogProcessor::ForceFlush(std::chrono::microseconds timeout) noexcept +{ + if (is_shutdown_.load() == true) + { + return false; + } + + is_force_flush_ = true; + + // Keep attempting to wake up the worker thread + while (is_force_flush_.load() == true) + { + cv_.notify_one(); + } + + // Now wait for the worker thread to signal back from the Export method + std::unique_lock lk(force_flush_cv_m_); + while (is_force_flush_notified_.load() == false) + { + force_flush_cv_.wait(lk); + } + + // Notify the worker thread + is_force_flush_notified_ = false; + + return true; +} + +void BatchLogProcessor::DoBackgroundWork() +{ + auto timeout = scheduled_delay_millis_; + + while (true) + { + // Wait for `timeout` milliseconds + std::unique_lock lk(cv_m_); + cv_.wait_for(lk, timeout); + + if (is_shutdown_.load() == true) + { + DrainQueue(); + return; + } + + bool was_force_flush_called = is_force_flush_.load(); + + // Check if this export was the result of a force flush. + if (was_force_flush_called == true) + { + // Since this export was the result of a force flush, signal the + // main thread that the worker thread has been notified + is_force_flush_ = false; + } + else + { + // If the buffer was empty during the entire `timeout` time interval, + // go back to waiting. If this was a spurious wake-up, we export only if + // `buffer_` is not empty. This is acceptable because batching is a best + // mechanism effort here. + if (buffer_.empty() == true) + { + continue; + } + } + + auto start = std::chrono::steady_clock::now(); + Export(was_force_flush_called); + auto end = std::chrono::steady_clock::now(); + auto duration = std::chrono::duration_cast(end - start); + + // Subtract the duration of this export call from the next `timeout`. + timeout = scheduled_delay_millis_ - duration; + } +} + +void BatchLogProcessor::Export(const bool was_force_flush_called) +{ + std::vector> records_arr; + + size_t num_records_to_export; + + if (was_force_flush_called == true) + { + num_records_to_export = buffer_.size(); + } + else + { + num_records_to_export = + buffer_.size() >= max_export_batch_size_ ? max_export_batch_size_ : buffer_.size(); + } + + buffer_.Consume(num_records_to_export, + [&](CircularBufferRange> range) noexcept { + range.ForEach([&](AtomicUniquePtr &ptr) { + std::unique_ptr swap_ptr = std::unique_ptr(nullptr); + ptr.Swap(swap_ptr); + records_arr.push_back(std::unique_ptr(swap_ptr.release())); + return true; + }); + }); + + exporter_->Export( + nostd::span>(records_arr.data(), records_arr.size())); + + // Notify the main thread in case this export was the result of a force flush. + if (was_force_flush_called == true) + { + is_force_flush_notified_ = true; + while (is_force_flush_notified_.load() == true) + { + force_flush_cv_.notify_one(); + } + } +} + +void BatchLogProcessor::DrainQueue() +{ + while (buffer_.empty() == false) + { + Export(false); + } +} + +bool BatchLogProcessor::Shutdown(std::chrono::microseconds timeout) noexcept +{ + std::lock_guard shutdown_guard{shutdown_m_}; + bool already_shutdown = is_shutdown_.exchange(true); + + if (worker_thread_.joinable()) + { + cv_.notify_one(); + worker_thread_.join(); + } + + // Should only shutdown exporter ONCE. + if (!already_shutdown && exporter_ != nullptr) + { + return exporter_->Shutdown(); + } + + return true; +} + +BatchLogProcessor::~BatchLogProcessor() +{ + if (is_shutdown_.load() == false) + { + Shutdown(); + } +} + +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/logger.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/logger.cc new file mode 100644 index 000000000..b8fdb15c3 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/logger.cc @@ -0,0 +1,126 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW +# include "opentelemetry/sdk/logs/logger.h" +# include "opentelemetry/sdk/logs/log_record.h" +# include "opentelemetry/sdk_config.h" +# include "opentelemetry/trace/provider.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ +namespace trace_api = opentelemetry::trace; +namespace nostd = opentelemetry::nostd; +namespace common = opentelemetry::common; + +Logger::Logger(nostd::string_view name, + std::shared_ptr context, + std::unique_ptr + instrumentation_library) noexcept + : logger_name_(std::string(name)), + instrumentation_library_(std::move(instrumentation_library)), + context_(context) +{} + +const nostd::string_view Logger::GetName() noexcept +{ + return logger_name_; +} + +/** + * Create and populate recordable with the log event's fields passed in. + * The timestamp, severity, traceid, spanid, and traceflags, are injected + * if the user does not specify them. + */ +void Logger::Log(opentelemetry::logs::Severity severity, + nostd::string_view body, + const common::KeyValueIterable &attributes, + trace_api::TraceId trace_id, + trace_api::SpanId span_id, + trace_api::TraceFlags trace_flags, + common::SystemTimestamp timestamp) noexcept +{ + // If this logger does not have a processor, no need to create a log record + if (!context_) + { + return; + } + auto &processor = context_->GetProcessor(); + + // TODO: Sampler (should include check for minSeverity) + + auto recordable = processor.MakeRecordable(); + if (recordable == nullptr) + { + OTEL_INTERNAL_LOG_ERROR("[LOGGER] Recordable creation failed"); + return; + } + + // Populate recordable fields + recordable->SetTimestamp(timestamp); + recordable->SetSeverity(severity); + recordable->SetBody(body); + recordable->SetInstrumentationLibrary(GetInstrumentationLibrary()); + + recordable->SetResource(context_->GetResource()); + + attributes.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept { + recordable->SetAttribute(key, value); + return true; + }); + + // Inject trace_id/span_id/trace_flags if none is set by user + auto provider = trace_api::Provider::GetTracerProvider(); + auto tracer = provider->GetTracer(logger_name_); + auto span_context = tracer->GetCurrentSpan()->GetContext(); + + // Leave these fields in the recordable empty if neither the passed in values + // nor the context values are valid (e.g. the application is not using traces) + + // TraceId + if (trace_id.IsValid()) + { + recordable->SetTraceId(trace_id); + } + else if (span_context.trace_id().IsValid()) + { + recordable->SetTraceId(span_context.trace_id()); + } + + // SpanId + if (span_id.IsValid()) + { + recordable->SetSpanId(span_id); + } + else if (span_context.span_id().IsValid()) + { + recordable->SetSpanId(span_context.span_id()); + } + + // TraceFlags + if (trace_flags.IsSampled()) + { + recordable->SetTraceFlags(trace_flags); + } + else if (span_context.trace_flags().IsSampled()) + { + recordable->SetTraceFlags(span_context.trace_flags()); + } + + // Send the log record to the processor + processor.OnReceive(std::move(recordable)); +} + +const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary & +Logger::GetInstrumentationLibrary() const noexcept +{ + return *instrumentation_library_; +} + +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/logger_context.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/logger_context.cc new file mode 100644 index 000000000..b0025ff72 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/logger_context.cc @@ -0,0 +1,54 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/logs/logger_context.h" +# include "opentelemetry/sdk/logs/multi_log_processor.h" + +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ + +LoggerContext::LoggerContext(std::vector> &&processors, + opentelemetry::sdk::resource::Resource resource) noexcept + : resource_(resource), + processor_(std::unique_ptr(new MultiLogProcessor(std::move(processors)))) +{} + +void LoggerContext::AddProcessor(std::unique_ptr processor) noexcept +{ + auto multi_processor = static_cast(processor_.get()); + multi_processor->AddProcessor(std::move(processor)); +} + +LogProcessor &LoggerContext::GetProcessor() const noexcept +{ + return *processor_; +} + +const opentelemetry::sdk::resource::Resource &LoggerContext::GetResource() const noexcept +{ + return resource_; +} + +bool LoggerContext::ForceFlush(std::chrono::microseconds timeout) noexcept +{ + return processor_->ForceFlush(timeout); +} + +bool LoggerContext::Shutdown(std::chrono::microseconds timeout) noexcept +{ + return processor_->ForceFlush(timeout); +} + +} // namespace logs +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/logger_provider.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/logger_provider.cc new file mode 100644 index 000000000..089b8d0de --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/logger_provider.cc @@ -0,0 +1,142 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/logs/logger_provider.h" + +# include +# include +# include +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ + +namespace nostd = opentelemetry::nostd; +namespace logs_api = opentelemetry::logs; + +LoggerProvider::LoggerProvider(std::unique_ptr &&processor, + opentelemetry::sdk::resource::Resource resource) noexcept +{ + std::vector> processors; + processors.emplace_back(std::move(processor)); + context_ = std::make_shared(std::move(processors), std::move(resource)); +} + +LoggerProvider::LoggerProvider(std::vector> &&processors, + opentelemetry::sdk::resource::Resource resource) noexcept + : context_{ + std::make_shared(std::move(processors), std::move(resource))} +{} + +LoggerProvider::LoggerProvider() noexcept + : context_{ + std::make_shared(std::vector>{})} +{} + +LoggerProvider::LoggerProvider(std::shared_ptr context) noexcept + : context_{context} +{} + +LoggerProvider::~LoggerProvider() +{ + // Logger hold the shared pointer to the context. So we can not use destructor of LoggerContext to + // Shutdown and flush all pending recordables when we hasve more than one loggers.These + // recordables may use the raw pointer of instrumentation_library_ in Logger + if (context_) + { + context_->Shutdown(); + } +} + +nostd::shared_ptr LoggerProvider::GetLogger( + nostd::string_view logger_name, + nostd::string_view options, + nostd::string_view library_name, + nostd::string_view library_version, + nostd::string_view schema_url) noexcept +{ + // Ensure only one thread can read/write from the map of loggers + std::lock_guard lock_guard{lock_}; + + // If a logger with a name "logger_name" already exists, return it + for (auto &logger : loggers_) + { + auto &logger_lib = logger->GetInstrumentationLibrary(); + if (logger->GetName() == logger_name && + logger_lib.equal(library_name, library_version, schema_url)) + { + return nostd::shared_ptr{logger}; + } + } + + // Check if creating a new logger would exceed the max number of loggers + // TODO: Remove the noexcept from the API's and SDK's GetLogger(~) + /* + if (loggers_.size() > MAX_LOGGER_COUNT) + { +#if __EXCEPTIONS + throw std::length_error("Number of loggers exceeds max count"); +#else + std::terminate(); +#endif + } + */ + + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#field-instrumentationscope + opentelemetry::nostd::unique_ptr lib; + if (library_name.empty()) + { + lib = instrumentationlibrary::InstrumentationLibrary::Create(logger_name, library_version, + schema_url); + } + else + { + lib = instrumentationlibrary::InstrumentationLibrary::Create(library_name, library_version, + schema_url); + } + + loggers_.push_back(std::shared_ptr( + new Logger(logger_name, context_, std::move(lib)))); + return nostd::shared_ptr{loggers_.back()}; +} + +nostd::shared_ptr LoggerProvider::GetLogger( + nostd::string_view logger_name, + nostd::span args, + nostd::string_view library_name, + nostd::string_view library_version, + nostd::string_view schema_url) noexcept +{ + return GetLogger(logger_name, "", library_name, library_version, schema_url); +} + +void LoggerProvider::AddProcessor(std::unique_ptr processor) noexcept +{ + context_->AddProcessor(std::move(processor)); +} + +const opentelemetry::sdk::resource::Resource &LoggerProvider::GetResource() const noexcept +{ + return context_->GetResource(); +} + +bool LoggerProvider::Shutdown() noexcept +{ + return context_->Shutdown(); +} + +bool LoggerProvider::ForceFlush(std::chrono::microseconds timeout) noexcept +{ + return context_->ForceFlush(timeout); +} + +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/multi_log_processor.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/multi_log_processor.cc new file mode 100644 index 000000000..5c7fe400f --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/multi_log_processor.cc @@ -0,0 +1,151 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/logs/multi_log_processor.h" + +# include +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ + +MultiLogProcessor::MultiLogProcessor(std::vector> &&processors) +{ + for (auto &processor : processors) + { + AddProcessor(std::move(processor)); + } +} +MultiLogProcessor::~MultiLogProcessor() +{ + ForceFlush(); + Shutdown(); +} + +void MultiLogProcessor::AddProcessor(std::unique_ptr &&processor) +{ + // Add preocessor to end of the list. + if (processor) + { + processors_.emplace_back(std::move(processor)); + } +} + +std::unique_ptr MultiLogProcessor::MakeRecordable() noexcept +{ + auto recordable = std::unique_ptr(new MultiRecordable); + auto multi_recordable = static_cast(recordable.get()); + for (auto &processor : processors_) + { + multi_recordable->AddRecordable(*processor, processor->MakeRecordable()); + } + return recordable; +} + +void MultiLogProcessor::OnReceive(std::unique_ptr &&record) noexcept +{ + if (!record) + { + return; + } + auto multi_recordable = static_cast(record.get()); + + for (auto &processor : processors_) + { + auto recordable = multi_recordable->ReleaseRecordable(*processor); + if (recordable) + { + processor->OnReceive(std::move(recordable)); + } + } +} + +bool MultiLogProcessor::ForceFlush(std::chrono::microseconds timeout) noexcept +{ + // Converto nanos to prevent overflow + std::chrono::nanoseconds timeout_ns = std::chrono::nanoseconds::max(); + if (std::chrono::duration_cast(timeout_ns) > timeout) + { + timeout_ns = std::chrono::duration_cast(timeout); + } + bool result = true; + auto start_time = std::chrono::system_clock::now(); + auto overflow_checker = std::chrono::system_clock::time_point::max(); + std::chrono::system_clock::time_point expire_time; + if (overflow_checker - start_time <= timeout_ns) + { + expire_time = overflow_checker; + } + else + { + expire_time = + start_time + std::chrono::duration_cast(timeout_ns); + } + for (auto &processor : processors_) + { + if (!processor->ForceFlush(std::chrono::duration_cast(timeout_ns))) + { + result = false; + } + start_time = std::chrono::system_clock::now(); + if (expire_time > start_time) + { + timeout_ns = std::chrono::duration_cast(expire_time - start_time); + } + else + { + timeout_ns = std::chrono::nanoseconds::zero(); + } + } + return result; +} + +bool MultiLogProcessor::Shutdown(std::chrono::microseconds timeout) noexcept +{ + // Converto nanos to prevent overflow + std::chrono::nanoseconds timeout_ns = std::chrono::nanoseconds::max(); + if (std::chrono::duration_cast(timeout_ns) > timeout) + { + timeout_ns = std::chrono::duration_cast(timeout); + } + bool result = true; + auto start_time = std::chrono::system_clock::now(); + auto overflow_checker = std::chrono::system_clock::time_point::max(); + std::chrono::system_clock::time_point expire_time; + if (overflow_checker - start_time <= timeout_ns) + { + expire_time = overflow_checker; + } + else + { + expire_time = + start_time + std::chrono::duration_cast(timeout_ns); + } + for (auto &processor : processors_) + { + result |= + processor->Shutdown(std::chrono::duration_cast(timeout_ns)); + start_time = std::chrono::system_clock::now(); + if (expire_time > start_time) + { + timeout_ns = std::chrono::duration_cast(expire_time - start_time); + } + else + { + timeout_ns = std::chrono::nanoseconds::zero(); + } + } + return result; +} + +} // namespace logs +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/multi_recordable.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/multi_recordable.cc new file mode 100644 index 000000000..4d615ec73 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/multi_recordable.cc @@ -0,0 +1,140 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/logs/multi_recordable.h" + +# include +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ + +namespace +{ +std::size_t MakeKey(const opentelemetry::sdk::logs::LogProcessor &processor) +{ + return reinterpret_cast(&processor); +} + +} // namespace + +void MultiRecordable::AddRecordable(const LogProcessor &processor, + std::unique_ptr recordable) noexcept +{ + recordables_[MakeKey(processor)] = std::move(recordable); +} + +const std::unique_ptr &MultiRecordable::GetRecordable( + const LogProcessor &processor) const noexcept +{ + // TODO - return nullptr ref on failed lookup? + auto i = recordables_.find(MakeKey(processor)); + if (i != recordables_.end()) + { + return i->second; + } + static std::unique_ptr empty(nullptr); + return empty; +} + +std::unique_ptr MultiRecordable::ReleaseRecordable( + const LogProcessor &processor) noexcept +{ + auto i = recordables_.find(MakeKey(processor)); + if (i != recordables_.end()) + { + std::unique_ptr result(i->second.release()); + recordables_.erase(MakeKey(processor)); + return result; + } + return std::unique_ptr(nullptr); +} + +void MultiRecordable::SetTimestamp(opentelemetry::common::SystemTimestamp timestamp) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetTimestamp(timestamp); + } +} + +void MultiRecordable::SetSeverity(opentelemetry::logs::Severity severity) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetSeverity(severity); + } +} + +void MultiRecordable::SetBody(nostd::string_view message) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetBody(message); + } +} + +void MultiRecordable::SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetResource(resource); + } +} + +void MultiRecordable::SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetAttribute(key, value); + } +} + +void MultiRecordable::SetTraceId(opentelemetry::trace::TraceId trace_id) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetTraceId(trace_id); + } +} + +void MultiRecordable::SetSpanId(opentelemetry::trace::SpanId span_id) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetSpanId(span_id); + } +} + +void MultiRecordable::SetTraceFlags(opentelemetry::trace::TraceFlags trace_flags) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetTraceFlags(trace_flags); + } +} + +void MultiRecordable::SetInstrumentationLibrary( + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + &instrumentation_library) noexcept +{ + instrumentation_library_ = &instrumentation_library; +} + +const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary & +MultiRecordable::GetInstrumentationLibrary() const noexcept +{ + return *instrumentation_library_; +} +} // namespace logs +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/simple_log_processor.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/simple_log_processor.cc new file mode 100644 index 000000000..6e2fde9f1 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/logs/simple_log_processor.cc @@ -0,0 +1,64 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW +# include "opentelemetry/sdk/logs/simple_log_processor.h" + +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ +/** + * Initialize a simple log processor. + * @param exporter the configured exporter where log records are sent + */ +SimpleLogProcessor::SimpleLogProcessor(std::unique_ptr &&exporter) + : exporter_(std::move(exporter)) +{} + +std::unique_ptr SimpleLogProcessor::MakeRecordable() noexcept +{ + return exporter_->MakeRecordable(); +} + +/** + * Batches the log record it receives in a batch of 1 and immediately sends it + * to the configured exporter + */ +void SimpleLogProcessor::OnReceive(std::unique_ptr &&record) noexcept +{ + nostd::span> batch(&record, 1); + // Get lock to ensure Export() is never called concurrently + const std::lock_guard locked(lock_); + + if (exporter_->Export(batch) != sdk::common::ExportResult::kSuccess) + { + /* Alert user of the failed export */ + } +} +/** + * The simple processor does not have any log records to flush so this method is not used + */ +bool SimpleLogProcessor::ForceFlush(std::chrono::microseconds timeout) noexcept +{ + return true; +} + +bool SimpleLogProcessor::Shutdown(std::chrono::microseconds timeout) noexcept +{ + // Should only shutdown exporter ONCE. + if (!shutdown_latch_.test_and_set(std::memory_order_acquire) && exporter_ != nullptr) + { + return exporter_->Shutdown(timeout); + } + + return true; +} +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/BUILD new file mode 100644 index 000000000..e75ba6a1a --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/BUILD @@ -0,0 +1,28 @@ +# Copyright 2020, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "metrics", + srcs = glob(["**/*.cc"]), + hdrs = glob(["**/*.h"]), + include_prefix = "src/metrics", + deps = [ + "//api", + "//sdk:headers", + "//sdk/src/common:global_log_handler", + "//sdk/src/resource", + ], +) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/CMakeLists.txt new file mode 100644 index 000000000..77a371a80 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/CMakeLists.txt @@ -0,0 +1,29 @@ +add_library( + opentelemetry_metrics + meter_provider.cc + meter.cc + meter_context.cc + metric_reader.cc + export/periodic_exporting_metric_reader.cc + state/metric_collector.cc + state/sync_metric_storage.cc + state/temporal_metric_storage.cc + aggregation/histogram_aggregation.cc + aggregation/lastvalue_aggregation.cc + aggregation/sum_aggregation.cc + sync_instruments.cc) + +set_target_properties(opentelemetry_metrics PROPERTIES EXPORT_NAME metrics) + +target_link_libraries(opentelemetry_metrics PUBLIC opentelemetry_common) + +target_include_directories( + opentelemetry_metrics + PUBLIC "$") + +install( + TARGETS opentelemetry_metrics + EXPORT "${PROJECT_NAME}-target" + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/aggregation/histogram_aggregation.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/aggregation/histogram_aggregation.cc new file mode 100644 index 000000000..aa2be7471 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/aggregation/histogram_aggregation.cc @@ -0,0 +1,142 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h" +# include "opentelemetry/version.h" + +# include +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +LongHistogramAggregation::LongHistogramAggregation() +{ + point_data_.boundaries_ = std::list{0l, 5l, 10l, 25l, 50l, 75l, 100l, 250l, 500l, 1000l}; + point_data_.counts_ = + std::vector(nostd::get>(point_data_.boundaries_).size() + 1, 0); + point_data_.sum_ = 0l; + point_data_.count_ = 0; +} + +LongHistogramAggregation::LongHistogramAggregation(HistogramPointData &&data) + : point_data_{std::move(data)} +{} + +LongHistogramAggregation::LongHistogramAggregation(const HistogramPointData &data) + : point_data_{data} +{} + +void LongHistogramAggregation::Aggregate(long value, const PointAttributes &attributes) noexcept +{ + const std::lock_guard locked(lock_); + point_data_.count_ += 1; + point_data_.sum_ = nostd::get(point_data_.sum_) + value; + size_t index = 0; + for (auto it = nostd::get>(point_data_.boundaries_).begin(); + it != nostd::get>(point_data_.boundaries_).end(); ++it) + { + if (value < *it) + { + point_data_.counts_[index] += 1; + return; + } + index++; + } +} + +std::unique_ptr LongHistogramAggregation::Merge( + const Aggregation &delta) const noexcept +{ + auto curr_value = nostd::get(ToPoint()); + auto delta_value = nostd::get( + (static_cast(delta).ToPoint())); + LongHistogramAggregation *aggr = new LongHistogramAggregation(); + HistogramMerge(curr_value, delta_value, aggr->point_data_); + return std::unique_ptr(aggr); +} + +std::unique_ptr LongHistogramAggregation::Diff(const Aggregation &next) const noexcept +{ + auto curr_value = nostd::get(ToPoint()); + auto next_value = nostd::get( + (static_cast(next).ToPoint())); + LongHistogramAggregation *aggr = new LongHistogramAggregation(); + HistogramDiff(curr_value, next_value, aggr->point_data_); + return std::unique_ptr(aggr); +} + +PointType LongHistogramAggregation::ToPoint() const noexcept +{ + return point_data_; +} + +DoubleHistogramAggregation::DoubleHistogramAggregation() +{ + point_data_.boundaries_ = + std::list{0.0, 5.0, 10.0, 25.0, 50.0, 75.0, 100.0, 250.0, 500.0, 1000.0}; + point_data_.counts_ = + std::vector(nostd::get>(point_data_.boundaries_).size() + 1, 0); + point_data_.sum_ = 0.0; + point_data_.count_ = 0; +} + +DoubleHistogramAggregation::DoubleHistogramAggregation(HistogramPointData &&data) + : point_data_{std::move(data)} +{} + +DoubleHistogramAggregation::DoubleHistogramAggregation(const HistogramPointData &data) + : point_data_{data} +{} + +void DoubleHistogramAggregation::Aggregate(double value, const PointAttributes &attributes) noexcept +{ + const std::lock_guard locked(lock_); + point_data_.count_ += 1; + point_data_.sum_ = nostd::get(point_data_.sum_) + value; + size_t index = 0; + for (auto it = nostd::get>(point_data_.boundaries_).begin(); + it != nostd::get>(point_data_.boundaries_).end(); ++it) + { + if (value < *it) + { + point_data_.counts_[index] += 1; + return; + } + index++; + } +} + +std::unique_ptr DoubleHistogramAggregation::Merge( + const Aggregation &delta) const noexcept +{ + auto curr_value = nostd::get(ToPoint()); + auto delta_value = nostd::get( + (static_cast(delta).ToPoint())); + DoubleHistogramAggregation *aggr = new DoubleHistogramAggregation(); + HistogramMerge(curr_value, delta_value, aggr->point_data_); + return std::unique_ptr(aggr); +} + +std::unique_ptr DoubleHistogramAggregation::Diff( + const Aggregation &next) const noexcept +{ + auto curr_value = nostd::get(ToPoint()); + auto next_value = nostd::get( + (static_cast(next).ToPoint())); + DoubleHistogramAggregation *aggr = new DoubleHistogramAggregation(); + HistogramDiff(curr_value, next_value, aggr->point_data_); + return std::unique_ptr(aggr); +} + +PointType DoubleHistogramAggregation::ToPoint() const noexcept +{ + return point_data_; +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/aggregation/lastvalue_aggregation.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/aggregation/lastvalue_aggregation.cc new file mode 100644 index 000000000..a12500533 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/aggregation/lastvalue_aggregation.cc @@ -0,0 +1,134 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/aggregation/lastvalue_aggregation.h" +# include "opentelemetry/common/timestamp.h" +# include "opentelemetry/version.h" + +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +LongLastValueAggregation::LongLastValueAggregation() +{ + point_data_.is_lastvalue_valid_ = false; + point_data_.value_ = 0l; +} + +LongLastValueAggregation::LongLastValueAggregation(LastValuePointData &&data) + : point_data_{std::move(data)} +{} + +LongLastValueAggregation::LongLastValueAggregation(const LastValuePointData &data) + : point_data_{data} +{} + +void LongLastValueAggregation::Aggregate(long value, const PointAttributes &attributes) noexcept +{ + const std::lock_guard locked(lock_); + point_data_.is_lastvalue_valid_ = true; + point_data_.value_ = value; +} + +std::unique_ptr LongLastValueAggregation::Merge( + const Aggregation &delta) const noexcept +{ + if (nostd::get(ToPoint()).sample_ts_.time_since_epoch() > + nostd::get(delta.ToPoint()).sample_ts_.time_since_epoch()) + { + LastValuePointData merge_data = std::move(nostd::get(ToPoint())); + return std::unique_ptr(new LongLastValueAggregation(std::move(merge_data))); + } + else + { + LastValuePointData merge_data = std::move(nostd::get(delta.ToPoint())); + return std::unique_ptr(new LongLastValueAggregation(std::move(merge_data))); + } +} + +std::unique_ptr LongLastValueAggregation::Diff(const Aggregation &next) const noexcept +{ + if (nostd::get(ToPoint()).sample_ts_.time_since_epoch() > + nostd::get(next.ToPoint()).sample_ts_.time_since_epoch()) + { + LastValuePointData diff_data = std::move(nostd::get(ToPoint())); + return std::unique_ptr(new LongLastValueAggregation(std::move(diff_data))); + } + else + { + LastValuePointData diff_data = std::move(nostd::get(next.ToPoint())); + return std::unique_ptr(new LongLastValueAggregation(std::move(diff_data))); + } +} + +PointType LongLastValueAggregation::ToPoint() const noexcept +{ + return point_data_; +} + +DoubleLastValueAggregation::DoubleLastValueAggregation() +{ + point_data_.is_lastvalue_valid_ = false; + point_data_.value_ = 0.0; +} + +DoubleLastValueAggregation::DoubleLastValueAggregation(LastValuePointData &&data) + : point_data_{std::move(data)} +{} + +DoubleLastValueAggregation::DoubleLastValueAggregation(const LastValuePointData &data) + : point_data_{data} +{} + +void DoubleLastValueAggregation::Aggregate(double value, const PointAttributes &attributes) noexcept +{ + const std::lock_guard locked(lock_); + point_data_.is_lastvalue_valid_ = true; + point_data_.value_ = value; +} + +std::unique_ptr DoubleLastValueAggregation::Merge( + const Aggregation &delta) const noexcept +{ + if (nostd::get(ToPoint()).sample_ts_.time_since_epoch() > + nostd::get(delta.ToPoint()).sample_ts_.time_since_epoch()) + { + LastValuePointData merge_data = std::move(nostd::get(ToPoint())); + return std::unique_ptr(new LongLastValueAggregation(std::move(merge_data))); + } + else + { + LastValuePointData merge_data = std::move(nostd::get(delta.ToPoint())); + return std::unique_ptr(new LongLastValueAggregation(std::move(merge_data))); + } +} + +std::unique_ptr DoubleLastValueAggregation::Diff( + const Aggregation &next) const noexcept +{ + if (nostd::get(ToPoint()).sample_ts_.time_since_epoch() > + nostd::get(next.ToPoint()).sample_ts_.time_since_epoch()) + { + LastValuePointData diff_data = std::move(nostd::get(ToPoint())); + return std::unique_ptr(new LongLastValueAggregation(std::move(diff_data))); + } + else + { + LastValuePointData diff_data = std::move(nostd::get(next.ToPoint())); + return std::unique_ptr(new LongLastValueAggregation(std::move(diff_data))); + } +} + +PointType DoubleLastValueAggregation::ToPoint() const noexcept +{ + return point_data_; +} +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/aggregation/sum_aggregation.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/aggregation/sum_aggregation.cc new file mode 100644 index 000000000..5ca786496 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/aggregation/sum_aggregation.cc @@ -0,0 +1,111 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/aggregation/sum_aggregation.h" +# include "opentelemetry/sdk/metrics/data/point_data.h" +# include "opentelemetry/version.h" + +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +LongSumAggregation::LongSumAggregation() +{ + point_data_.value_ = 0l; +} + +LongSumAggregation::LongSumAggregation(SumPointData &&data) : point_data_{std::move(data)} {} + +LongSumAggregation::LongSumAggregation(const SumPointData &data) : point_data_{data} {} + +void LongSumAggregation::Aggregate(long value, const PointAttributes &attributes) noexcept +{ + const std::lock_guard locked(lock_); + point_data_.value_ = nostd::get(point_data_.value_) + value; +} + +std::unique_ptr LongSumAggregation::Merge(const Aggregation &delta) const noexcept +{ + long merge_value = + nostd::get( + nostd::get((static_cast(delta).ToPoint())) + .value_) + + nostd::get(nostd::get(ToPoint()).value_); + std::unique_ptr aggr(new LongSumAggregation()); + static_cast(aggr.get())->point_data_.value_ = merge_value; + return aggr; +} + +std::unique_ptr LongSumAggregation::Diff(const Aggregation &next) const noexcept +{ + + long diff_value = nostd::get(nostd::get( + (static_cast(next).ToPoint())) + .value_) - + nostd::get(nostd::get(ToPoint()).value_); + std::unique_ptr aggr(new LongSumAggregation()); + static_cast(aggr.get())->point_data_.value_ = diff_value; + return aggr; +} + +PointType LongSumAggregation::ToPoint() const noexcept +{ + return point_data_; +} + +DoubleSumAggregation::DoubleSumAggregation() +{ + point_data_.value_ = 0.0; +} + +DoubleSumAggregation::DoubleSumAggregation(SumPointData &&data) : point_data_(std::move(data)) {} + +DoubleSumAggregation::DoubleSumAggregation(const SumPointData &data) : point_data_(data) {} + +void DoubleSumAggregation::Aggregate(double value, const PointAttributes &attributes) noexcept +{ + const std::lock_guard locked(lock_); + point_data_.value_ = nostd::get(point_data_.value_) + value; +} + +std::unique_ptr DoubleSumAggregation::Merge(const Aggregation &delta) const noexcept +{ + double merge_value = + nostd::get( + nostd::get((static_cast(delta).ToPoint())) + .value_) + + nostd::get(nostd::get(ToPoint()).value_); + std::unique_ptr aggr(new DoubleSumAggregation()); + static_cast(aggr.get())->point_data_.value_ = merge_value; + return aggr; +} + +std::unique_ptr DoubleSumAggregation::Diff(const Aggregation &next) const noexcept +{ + + double diff_value = + nostd::get( + nostd::get((static_cast(next).ToPoint())) + .value_) - + nostd::get(nostd::get(ToPoint()).value_); + std::unique_ptr aggr(new DoubleSumAggregation()); + static_cast(aggr.get())->point_data_.value_ = diff_value; + return aggr; +} + +PointType DoubleSumAggregation::ToPoint() const noexcept +{ + const std::lock_guard locked(lock_); + return point_data_; +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/export/periodic_exporting_metric_reader.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/export/periodic_exporting_metric_reader.cc new file mode 100644 index 000000000..f11f84544 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/export/periodic_exporting_metric_reader.cc @@ -0,0 +1,101 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.h" +# include "opentelemetry/sdk/common/global_log_handler.h" +# include "opentelemetry/sdk/metrics/metric_exporter.h" + +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +PeriodicExportingMetricReader::PeriodicExportingMetricReader( + std::unique_ptr exporter, + const PeriodicExportingMetricReaderOptions &option, + AggregationTemporality aggregation_temporality) + : MetricReader(aggregation_temporality), + exporter_{std::move(exporter)}, + export_interval_millis_{option.export_interval_millis}, + export_timeout_millis_{option.export_timeout_millis} +{ + if (export_interval_millis_ <= export_timeout_millis_) + { + OTEL_INTERNAL_LOG_WARN( + "[Periodic Exporting Metric Reader] Invalid configuration: " + "export_interval_millis_ should be less than export_timeout_millis_, using default values"); + export_interval_millis_ = kExportIntervalMillis; + export_timeout_millis_ = kExportTimeOutMillis; + } +} + +void PeriodicExportingMetricReader::OnInitialized() noexcept +{ + worker_thread_ = std::thread(&PeriodicExportingMetricReader::DoBackgroundWork, this); +} + +void PeriodicExportingMetricReader::DoBackgroundWork() +{ + std::unique_lock lk(cv_m_); + do + { + if (IsShutdown()) + { + break; + } + std::atomic cancel_export_for_timeout{false}; + auto start = std::chrono::steady_clock::now(); + auto future_receive = std::async(std::launch::async, [this, &cancel_export_for_timeout] { + Collect([this, &cancel_export_for_timeout](ResourceMetrics &metric_data) { + if (cancel_export_for_timeout) + { + OTEL_INTERNAL_LOG_ERROR( + "[Periodic Exporting Metric Reader] Collect took longer configured time: " + << export_timeout_millis_.count() << " ms, and timed out"); + return false; + } + this->exporter_->Export(metric_data); + return true; + }); + }); + std::future_status status; + do + { + status = future_receive.wait_for(std::chrono::milliseconds(export_timeout_millis_)); + if (status == std::future_status::timeout) + { + cancel_export_for_timeout = true; + break; + } + } while (status != std::future_status::ready); + auto end = std::chrono::steady_clock::now(); + auto export_time_ms = std::chrono::duration_cast(end - start); + auto remaining_wait_interval_ms = export_interval_millis_ - export_time_ms; + cv_.wait_for(lk, remaining_wait_interval_ms); + } while (true); +} + +bool PeriodicExportingMetricReader::OnForceFlush(std::chrono::microseconds timeout) noexcept +{ + return exporter_->ForceFlush(timeout); +} + +bool PeriodicExportingMetricReader::OnShutDown(std::chrono::microseconds timeout) noexcept +{ + if (worker_thread_.joinable()) + { + cv_.notify_one(); + worker_thread_.join(); + } + return exporter_->Shutdown(timeout); +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/meter.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/meter.cc new file mode 100644 index 000000000..600bae986 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/meter.cc @@ -0,0 +1,259 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/meter.h" +# include "opentelemetry/metrics/noop.h" +# include "opentelemetry/nostd/shared_ptr.h" +# include "opentelemetry/sdk/metrics/async_instruments.h" +# include "opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h" +# include "opentelemetry/sdk/metrics/state/multi_metric_storage.h" +# include "opentelemetry/sdk/metrics/state/sync_metric_storage.h" +# include "opentelemetry/sdk/metrics/sync_instruments.h" +# include "opentelemetry/sdk_config.h" + +# include "opentelemetry/version.h" + +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +namespace metrics = opentelemetry::metrics; +namespace nostd = opentelemetry::nostd; + +Meter::Meter(std::shared_ptr meter_context, + std::unique_ptr + instrumentation_library) noexcept + : instrumentation_library_{std::move(instrumentation_library)}, meter_context_{meter_context} +{} + +nostd::shared_ptr> Meter::CreateLongCounter(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit) noexcept +{ + InstrumentDescriptor instrument_descriptor = { + std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, + std::string{unit.data(), unit.size()}, InstrumentType::kCounter, InstrumentValueType::kLong}; + auto storage = RegisterMetricStorage(instrument_descriptor); + return nostd::shared_ptr>( + new LongCounter(instrument_descriptor, std::move(storage))); +} + +nostd::shared_ptr> Meter::CreateDoubleCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit) noexcept +{ + InstrumentDescriptor instrument_descriptor = { + std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, + std::string{unit.data(), unit.size()}, InstrumentType::kCounter, + InstrumentValueType::kDouble}; + auto storage = RegisterMetricStorage(instrument_descriptor); + return nostd::shared_ptr>{ + new DoubleCounter(instrument_descriptor, std::move(storage))}; +} + +void Meter::CreateLongObservableCounter(nostd::string_view name, + void (*callback)(metrics::ObserverResult &, void *), + nostd::string_view description, + nostd::string_view unit, + void *state) noexcept +{ + InstrumentDescriptor instrument_descriptor = { + std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, + std::string{unit.data(), unit.size()}, InstrumentType::kObservableCounter, + InstrumentValueType::kLong}; + RegisterAsyncMetricStorage(instrument_descriptor, callback, state); +} + +void Meter::CreateDoubleObservableCounter(nostd::string_view name, + void (*callback)(metrics::ObserverResult &, + void *), + nostd::string_view description, + nostd::string_view unit, + void *state) noexcept +{ + InstrumentDescriptor instrument_descriptor = { + std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, + std::string{unit.data(), unit.size()}, InstrumentType::kObservableCounter, + InstrumentValueType::kDouble}; + RegisterAsyncMetricStorage(instrument_descriptor, callback, state); +} + +nostd::shared_ptr> Meter::CreateLongHistogram( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit) noexcept +{ + InstrumentDescriptor instrument_descriptor = { + std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, + std::string{unit.data(), unit.size()}, InstrumentType::kHistogram, + InstrumentValueType::kLong}; + auto storage = RegisterMetricStorage(instrument_descriptor); + return nostd::shared_ptr>{ + new LongHistogram(instrument_descriptor, std::move(storage))}; +} + +nostd::shared_ptr> Meter::CreateDoubleHistogram( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit) noexcept +{ + InstrumentDescriptor instrument_descriptor = { + std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, + std::string{unit.data(), unit.size()}, InstrumentType::kHistogram, + InstrumentValueType::kDouble}; + auto storage = RegisterMetricStorage(instrument_descriptor); + return nostd::shared_ptr>{ + new DoubleHistogram(instrument_descriptor, std::move(storage))}; +} + +void Meter::CreateLongObservableGauge(nostd::string_view name, + void (*callback)(metrics::ObserverResult &, void *), + nostd::string_view description, + nostd::string_view unit, + void *state) noexcept +{ + InstrumentDescriptor instrument_descriptor = { + std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, + std::string{unit.data(), unit.size()}, InstrumentType::kObservableGauge, + InstrumentValueType::kLong}; + RegisterAsyncMetricStorage(instrument_descriptor, callback, state); +} + +void Meter::CreateDoubleObservableGauge(nostd::string_view name, + void (*callback)(metrics::ObserverResult &, void *), + nostd::string_view description, + nostd::string_view unit, + void *state) noexcept +{ + InstrumentDescriptor instrument_descriptor = { + std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, + std::string{unit.data(), unit.size()}, InstrumentType::kObservableGauge, + InstrumentValueType::kDouble}; + RegisterAsyncMetricStorage(instrument_descriptor, callback, state); +} + +nostd::shared_ptr> Meter::CreateLongUpDownCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit) noexcept +{ + InstrumentDescriptor instrument_descriptor = { + std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, + std::string{unit.data(), unit.size()}, InstrumentType::kUpDownCounter, + InstrumentValueType::kLong}; + auto storage = RegisterMetricStorage(instrument_descriptor); + return nostd::shared_ptr>{ + new LongUpDownCounter(instrument_descriptor, std::move(storage))}; +} + +nostd::shared_ptr> Meter::CreateDoubleUpDownCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit) noexcept +{ + InstrumentDescriptor instrument_descriptor = { + std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, + std::string{unit.data(), unit.size()}, InstrumentType::kUpDownCounter, + InstrumentValueType::kDouble}; + auto storage = RegisterMetricStorage(instrument_descriptor); + return nostd::shared_ptr>{ + new DoubleUpDownCounter(instrument_descriptor, std::move(storage))}; +} + +void Meter::CreateLongObservableUpDownCounter(nostd::string_view name, + void (*callback)(metrics::ObserverResult &, + void *), + nostd::string_view description, + nostd::string_view unit, + void *state) noexcept +{ + InstrumentDescriptor instrument_descriptor = { + std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, + std::string{unit.data(), unit.size()}, InstrumentType::kObservableUpDownCounter, + InstrumentValueType::kLong}; + RegisterAsyncMetricStorage(instrument_descriptor, callback, state); +} + +void Meter::CreateDoubleObservableUpDownCounter(nostd::string_view name, + void (*callback)(metrics::ObserverResult &, + void *), + nostd::string_view description, + nostd::string_view unit, + void *state) noexcept +{ + InstrumentDescriptor instrument_descriptor = { + std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, + std::string{unit.data(), unit.size()}, InstrumentType::kObservableUpDownCounter, + InstrumentValueType::kDouble}; + RegisterAsyncMetricStorage(instrument_descriptor, callback, state); +} + +const sdk::instrumentationlibrary::InstrumentationLibrary *Meter::GetInstrumentationLibrary() + const noexcept +{ + return instrumentation_library_.get(); +} + +std::unique_ptr Meter::RegisterMetricStorage( + InstrumentDescriptor &instrument_descriptor) +{ + auto view_registry = meter_context_->GetViewRegistry(); + std::unique_ptr storages(new MultiMetricStorage()); + + auto success = view_registry->FindViews( + instrument_descriptor, *instrumentation_library_, + [this, &instrument_descriptor, &storages](const View &view) { + auto view_instr_desc = instrument_descriptor; + if (!view.GetName().empty()) + { + view_instr_desc.name_ = view.GetName(); + } + if (!view.GetDescription().empty()) + { + view_instr_desc.description_ = view.GetDescription(); + } + auto storage = std::shared_ptr(new SyncMetricStorage( + view_instr_desc, view.GetAggregationType(), &view.GetAttributesProcessor(), + NoExemplarReservoir::GetNoExemplarReservoir())); + storage_registry_[instrument_descriptor.name_] = storage; + auto multi_storage = static_cast(storages.get()); + multi_storage->AddStorage(storage); + return true; + }); + + if (!success) + { + OTEL_INTERNAL_LOG_ERROR( + "[Meter::RegisterMetricStorage] - Error during finding matching views." + << "Some of the matching view configurations mayn't be used for metric collection"); + } + return storages; +} + +/** collect metrics across all the meters **/ +std::vector Meter::Collect(CollectorHandle *collector, + opentelemetry::common::SystemTimestamp collect_ts) noexcept +{ + std::vector metric_data_list; + for (auto &metric_storage : storage_registry_) + { + metric_storage.second->Collect(collector, meter_context_->GetCollectors(), + meter_context_->GetSDKStartTime(), collect_ts, + [&metric_data_list](MetricData metric_data) { + metric_data_list.push_back(metric_data); + return true; + }); + } + return metric_data_list; +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/meter_context.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/meter_context.cc new file mode 100644 index 000000000..73721e324 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/meter_context.cc @@ -0,0 +1,108 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/meter_context.h" +# include "opentelemetry/sdk/common/global_log_handler.h" +# include "opentelemetry/sdk/metrics/metric_reader.h" +# include "opentelemetry/sdk_config.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +MeterContext::MeterContext(std::unique_ptr views, + opentelemetry::sdk::resource::Resource resource) noexcept + : resource_{resource}, views_(std::move(views)), sdk_start_ts_{std::chrono::system_clock::now()} +{} + +const resource::Resource &MeterContext::GetResource() const noexcept +{ + return resource_; +} + +ViewRegistry *MeterContext::GetViewRegistry() const noexcept +{ + return views_.get(); +} + +nostd::span> MeterContext::GetMeters() noexcept +{ + return nostd::span>{meters_}; +} + +nostd::span> MeterContext::GetCollectors() noexcept +{ + return nostd::span>(collectors_); +} + +opentelemetry::common::SystemTimestamp MeterContext::GetSDKStartTime() noexcept +{ + return sdk_start_ts_; +} + +void MeterContext::AddMetricReader(std::unique_ptr reader) noexcept +{ + auto collector = + std::shared_ptr{new MetricCollector(shared_from_this(), std::move(reader))}; + collectors_.push_back(collector); +} + +void MeterContext::AddView(std::unique_ptr instrument_selector, + std::unique_ptr meter_selector, + std::unique_ptr view) noexcept +{ + views_->AddView(std::move(instrument_selector), std::move(meter_selector), std::move(view)); +} + +void MeterContext::AddMeter(std::shared_ptr meter) +{ + meters_.push_back(meter); +} + +bool MeterContext::Shutdown() noexcept +{ + bool result = true; + if (!shutdown_latch_.test_and_set(std::memory_order_acquire)) + { + + for (auto &collector : collectors_) + { + bool status = std::static_pointer_cast(collector)->Shutdown(); + result = result && status; + } + if (!result) + { + OTEL_INTERNAL_LOG_WARN("[MeterContext::Shutdown] Unable to shutdown all metric readers"); + } + } + return result; +} + +bool MeterContext::ForceFlush(std::chrono::microseconds timeout) noexcept +{ + // TODO - Implement timeout logic. + bool result = true; + if (!shutdown_latch_.test_and_set(std::memory_order_acquire)) + { + + for (auto &collector : collectors_) + { + bool status = std::static_pointer_cast(collector)->ForceFlush(timeout); + result = result && status; + } + if (!result) + { + OTEL_INTERNAL_LOG_WARN("[MeterContext::ForceFlush] Unable to ForceFlush all metric readers"); + } + } + return result; +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/meter_provider.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/meter_provider.cc new file mode 100644 index 000000000..788811cd6 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/meter_provider.cc @@ -0,0 +1,94 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/meter_provider.h" +# include "opentelemetry/metrics/meter.h" +# include "opentelemetry/sdk/metrics/metric_reader.h" + +# include "opentelemetry/sdk/common/global_log_handler.h" +# include "opentelemetry/sdk_config.h" +# include "opentelemetry/version.h" + +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +namespace resource = opentelemetry::sdk::resource; +namespace metrics_api = opentelemetry::metrics; + +MeterProvider::MeterProvider(std::shared_ptr context) noexcept : context_{context} {} + +MeterProvider::MeterProvider(std::unique_ptr views, + sdk::resource::Resource resource) noexcept + : context_(std::make_shared(std::move(views), resource)) +{} + +nostd::shared_ptr MeterProvider::GetMeter( + nostd::string_view name, + nostd::string_view version, + nostd::string_view schema_url) noexcept +{ + if (name.data() == nullptr || name == "") + { + OTEL_INTERNAL_LOG_WARN("[MeterProvider::GetMeter] Library name is empty."); + name = ""; + } + + const std::lock_guard guard(lock_); + + for (auto &meter : context_->GetMeters()) + { + auto meter_lib = meter->GetInstrumentationLibrary(); + if (meter_lib->equal(name, version, schema_url)) + { + return nostd::shared_ptr{meter}; + } + } + auto lib = instrumentationlibrary::InstrumentationLibrary::Create(name, version, schema_url); + auto meter = std::shared_ptr(new Meter(context_, std::move(lib))); + context_->AddMeter(meter); + return nostd::shared_ptr{meter}; +} + +const resource::Resource &MeterProvider::GetResource() const noexcept +{ + return context_->GetResource(); +} + +void MeterProvider::AddMetricReader(std::unique_ptr reader) noexcept +{ + return context_->AddMetricReader(std::move(reader)); +} + +void MeterProvider::AddView(std::unique_ptr instrument_selector, + std::unique_ptr meter_selector, + std::unique_ptr view) noexcept +{ + return context_->AddView(std::move(instrument_selector), std::move(meter_selector), + std::move(view)); +} + +/** + * Shutdown the meter provider. + */ +bool MeterProvider::Shutdown() noexcept +{ + return context_->Shutdown(); +} + +/** + * Force flush the meter provider. + */ +bool MeterProvider::ForceFlush(std::chrono::microseconds timeout) noexcept +{ + return context_->ForceFlush(timeout); +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/metric_reader.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/metric_reader.cc new file mode 100644 index 000000000..c8d6de896 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/metric_reader.cc @@ -0,0 +1,94 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/metric_reader.h" +# include "opentelemetry/sdk/metrics/export/metric_producer.h" + +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +MetricReader::MetricReader(AggregationTemporality aggregation_temporality) + : aggregation_temporality_(aggregation_temporality), shutdown_(false), metric_producer_(nullptr) +{} + +void MetricReader::SetMetricProducer(MetricProducer *metric_producer) +{ + metric_producer_ = metric_producer; + OnInitialized(); +} + +AggregationTemporality MetricReader::GetAggregationTemporality() const noexcept +{ + return aggregation_temporality_; +} + +bool MetricReader::Collect( + nostd::function_ref callback) noexcept +{ + if (!metric_producer_) + { + OTEL_INTERNAL_LOG_WARN( + "MetricReader::Collect Cannot invoke Collect(). No MetricProducer registered for " + "collection!") + } + if (IsShutdown()) + { + OTEL_INTERNAL_LOG_WARN("MetricReader::Collect Cannot invoke Collect(). Shutdown in progress!"); + } + + return metric_producer_->Collect(callback); +} + +bool MetricReader::Shutdown(std::chrono::microseconds timeout) noexcept +{ + bool status = true; + if (IsShutdown()) + { + OTEL_INTERNAL_LOG_WARN("MetricReader::Shutdown - Cannot invoke shutdown twice!"); + } + + { + const std::lock_guard locked(lock_); + shutdown_ = true; + } + + if (!OnShutDown(timeout)) + { + status = false; + OTEL_INTERNAL_LOG_WARN("MetricReader::OnShutDown Shutdown failed. Will not be tried again!"); + } + return status; +} + +/** Flush metric read by this reader **/ +bool MetricReader::ForceFlush(std::chrono::microseconds timeout) noexcept +{ + bool status = true; + if (shutdown_) + { + OTEL_INTERNAL_LOG_WARN("MetricReader::Shutdown Cannot invoke Force flush on shutdown reader!"); + } + if (!OnForceFlush(timeout)) + { + status = false; + OTEL_INTERNAL_LOG_ERROR("MetricReader::OnForceFlush failed!"); + } + return status; +} + +bool MetricReader::IsShutdown() const noexcept +{ + const std::lock_guard locked(lock_); + return shutdown_; +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/state/metric_collector.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/state/metric_collector.cc new file mode 100644 index 000000000..6cae7bf12 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/state/metric_collector.cc @@ -0,0 +1,62 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/state/metric_collector.h" +# include "opentelemetry/sdk/common/global_log_handler.h" +# include "opentelemetry/sdk/metrics/meter.h" +# include "opentelemetry/sdk/metrics/meter_context.h" +# include "opentelemetry/sdk/metrics/metric_reader.h" +# include "opentelemetry/sdk_config.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +MetricCollector::MetricCollector( + std::shared_ptr &&context, + std::unique_ptr metric_reader) + : meter_context_{std::move(context)}, metric_reader_{std::move(metric_reader)} +{ + metric_reader_->SetMetricProducer(this); +} + +AggregationTemporality MetricCollector::GetAggregationTemporality() noexcept +{ + return metric_reader_->GetAggregationTemporality(); +} + +bool MetricCollector::Collect( + nostd::function_ref callback) noexcept +{ + ResourceMetrics resource_metrics; + for (auto &meter : meter_context_->GetMeters()) + { + auto collection_ts = std::chrono::system_clock::now(); + InstrumentationInfoMetrics instrumentation_info_metrics; + instrumentation_info_metrics.metric_data_ = meter->Collect(this, collection_ts); + instrumentation_info_metrics.instrumentation_library_ = meter->GetInstrumentationLibrary(); + resource_metrics.instrumentation_info_metric_data_.push_back(instrumentation_info_metrics); + } + resource_metrics.resource_ = &meter_context_->GetResource(); + callback(resource_metrics); + return true; +} + +bool MetricCollector::ForceFlush(std::chrono::microseconds timeout) noexcept +{ + return metric_reader_->ForceFlush(timeout); +} + +bool MetricCollector::Shutdown(std::chrono::microseconds timeout) noexcept +{ + return metric_reader_->Shutdown(timeout); +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/state/sync_metric_storage.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/state/sync_metric_storage.cc new file mode 100644 index 000000000..8c79f9d7c --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/state/sync_metric_storage.cc @@ -0,0 +1,36 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW + +# include "opentelemetry/sdk/metrics/state/sync_metric_storage.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +bool SyncMetricStorage::Collect(CollectorHandle *collector, + nostd::span> collectors, + opentelemetry::common::SystemTimestamp sdk_start_ts, + opentelemetry::common::SystemTimestamp collection_ts, + nostd::function_ref callback) noexcept +{ + opentelemetry::common::SystemTimestamp last_collection_ts = sdk_start_ts; + auto aggregation_temporarily = collector->GetAggregationTemporality(); + + // Add the current delta metrics to `unreported metrics stash` for all the collectors, + // this will also empty the delta metrics hashmap, and make it available for + // recordings + std::shared_ptr delta_metrics = std::move(attributes_hashmap_); + attributes_hashmap_.reset(new AttributesHashMap); + + return temporal_metric_storage_.buildMetrics(collector, collectors, sdk_start_ts, collection_ts, + std::move(delta_metrics), callback); +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/state/temporal_metric_storage.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/state/temporal_metric_storage.cc new file mode 100644 index 000000000..55e93e3d4 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/state/temporal_metric_storage.cc @@ -0,0 +1,131 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW + +# include "opentelemetry/sdk/metrics/state/temporal_metric_storage.h" +# include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +TemporalMetricStorage::TemporalMetricStorage(InstrumentDescriptor instrument_descriptor) + : instrument_descriptor_(instrument_descriptor) +{} + +bool TemporalMetricStorage::buildMetrics(CollectorHandle *collector, + nostd::span> collectors, + opentelemetry::common::SystemTimestamp sdk_start_ts, + opentelemetry::common::SystemTimestamp collection_ts, + std::shared_ptr delta_metrics, + nostd::function_ref callback) noexcept +{ + std::lock_guard guard(lock_); + opentelemetry::common::SystemTimestamp last_collection_ts = sdk_start_ts; + auto aggregation_temporarily = collector->GetAggregationTemporality(); + for (auto &col : collectors) + { + unreported_metrics_[col.get()].push_back(delta_metrics); + } + + // Get the unreported metrics for the `collector` from `unreported metrics stash` + // since last collection, this will also cleanup the unreported metrics for `collector` + // from the stash. + auto present = unreported_metrics_.find(collector); + if (present == unreported_metrics_.end()) + { + // no unreported metrics for the collector, return. + return true; + } + auto unreported_list = std::move(present->second); + // Iterate over the unreporter metrics for `collector` and store result in `merged_metrics` + std::unique_ptr merged_metrics(new AttributesHashMap); + for (auto &agg_hashmap : unreported_list) + { + agg_hashmap->GetAllEnteries( + [&merged_metrics, this](const MetricAttributes &attributes, Aggregation &aggregation) { + auto agg = merged_metrics->Get(attributes); + if (agg) + { + merged_metrics->Set(attributes, agg->Merge(aggregation)); + } + else + { + merged_metrics->Set( + attributes, + DefaultAggregation::CreateAggregation(instrument_descriptor_)->Merge(aggregation)); + merged_metrics->GetAllEnteries( + [](const MetricAttributes &attr, Aggregation &aggr) { return true; }); + } + return true; + }); + } + // Get the last reported metrics for the `collector` from `last reported metrics` stash + // - If the aggregation_temporarily for the collector is cumulative + // - Merge the last reported metrics with unreported metrics (which is in merged_metrics), + // Final result of merge would be in merged_metrics. + // - Move the final merge to the `last reported metrics` stash. + // - If the aggregation_temporarily is delta + // - Store the unreported metrics for `collector` (which is in merged_mtrics) to + // `last reported metrics` stash. + + auto reported = last_reported_metrics_.find(collector); + if (reported != last_reported_metrics_.end()) + { + last_collection_ts = last_reported_metrics_[collector].collection_ts; + auto last_aggr_hashmap = std::move(last_reported_metrics_[collector].attributes_map); + if (aggregation_temporarily == AggregationTemporality::kCumulative) + { + // merge current delta to previous cumulative + last_aggr_hashmap->GetAllEnteries( + [&merged_metrics, this](const MetricAttributes &attributes, Aggregation &aggregation) { + auto agg = merged_metrics->Get(attributes); + if (agg) + { + merged_metrics->Set(attributes, agg->Merge(aggregation)); + } + else + { + merged_metrics->Set(attributes, + DefaultAggregation::CreateAggregation(instrument_descriptor_)); + } + return true; + }); + } + last_reported_metrics_[collector] = + LastReportedMetrics{std::move(merged_metrics), collection_ts}; + } + else + { + merged_metrics->GetAllEnteries( + [](const MetricAttributes &attr, Aggregation &aggr) { return true; }); + last_reported_metrics_.insert( + std::make_pair(collector, LastReportedMetrics{std::move(merged_metrics), collection_ts})); + } + + // Generate the MetricData from the final merged_metrics, and invoke callback over it. + + AttributesHashMap *result_to_export = (last_reported_metrics_[collector]).attributes_map.get(); + MetricData metric_data; + metric_data.instrument_descriptor = instrument_descriptor_; + metric_data.start_ts = last_collection_ts; + metric_data.end_ts = collection_ts; + result_to_export->GetAllEnteries( + [&metric_data](const MetricAttributes &attributes, Aggregation &aggregation) { + PointDataAttributes point_data_attr; + point_data_attr.point_data = aggregation.ToPoint(); + point_data_attr.attributes = attributes; + metric_data.point_data_attr_.push_back(point_data_attr); + return true; + }); + return callback(metric_data); +} + +} // namespace metrics + +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/sync_instruments.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/sync_instruments.cc new file mode 100644 index 000000000..7cf9034cd --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/metrics/sync_instruments.cc @@ -0,0 +1,202 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/sync_instruments.h" +# include "opentelemetry/sdk/metrics/state/metric_storage.h" +# include "opentelemetry/sdk_config.h" + +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +LongCounter::LongCounter(InstrumentDescriptor instrument_descriptor, + std::unique_ptr storage) + : Synchronous(instrument_descriptor, std::move(storage)) +{} + +void LongCounter::Add(long value, + const opentelemetry::common::KeyValueIterable &attributes) noexcept +{ + auto context = opentelemetry::context::Context{}; + return storage_->RecordLong(value, attributes, context); +} + +void LongCounter::Add(long value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept +{ + return storage_->RecordLong(value, attributes, context); +} + +void LongCounter::Add(long value) noexcept +{ + auto context = opentelemetry::context::Context{}; + return storage_->RecordLong(value, context); +} + +void LongCounter::Add(long value, const opentelemetry::context::Context &context) noexcept +{ + return storage_->RecordLong(value, context); +} + +DoubleCounter::DoubleCounter(InstrumentDescriptor instrument_descriptor, + std::unique_ptr storage) + : Synchronous(instrument_descriptor, std::move(storage)) +{} + +void DoubleCounter::Add(double value, + const opentelemetry::common::KeyValueIterable &attributes) noexcept +{ + auto context = opentelemetry::context::Context{}; + return storage_->RecordDouble(value, attributes, context); +} + +void DoubleCounter::Add(double value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept +{ + return storage_->RecordDouble(value, attributes, context); +} + +void DoubleCounter::Add(double value) noexcept +{ + auto context = opentelemetry::context::Context{}; + return storage_->RecordDouble(value, context); +} + +void DoubleCounter::Add(double value, const opentelemetry::context::Context &context) noexcept +{ + return storage_->RecordDouble(value, context); +} + +LongUpDownCounter::LongUpDownCounter(InstrumentDescriptor instrument_descriptor, + std::unique_ptr storage) + : Synchronous(instrument_descriptor, std::move(storage)) +{} + +void LongUpDownCounter::Add(long value, + const opentelemetry::common::KeyValueIterable &attributes) noexcept +{ + auto context = opentelemetry::context::Context{}; + return storage_->RecordLong(value, attributes, context); +} + +void LongUpDownCounter::Add(long value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept +{ + return storage_->RecordLong(value, attributes, context); +} + +void LongUpDownCounter::Add(long value) noexcept +{ + auto context = opentelemetry::context::Context{}; + return storage_->RecordLong(value, context); +} + +void LongUpDownCounter::Add(long value, const opentelemetry::context::Context &context) noexcept +{ + return storage_->RecordLong(value, context); +} + +DoubleUpDownCounter::DoubleUpDownCounter(InstrumentDescriptor instrument_descriptor, + std::unique_ptr storage) + : Synchronous(instrument_descriptor, std::move(storage)) +{} + +void DoubleUpDownCounter::Add(double value, + const opentelemetry::common::KeyValueIterable &attributes) noexcept +{ + auto context = opentelemetry::context::Context{}; + return storage_->RecordDouble(value, attributes, context); +} + +void DoubleUpDownCounter::Add(double value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept +{ + return storage_->RecordDouble(value, attributes, context); +} + +void DoubleUpDownCounter::Add(double value) noexcept +{ + auto context = opentelemetry::context::Context{}; + return storage_->RecordDouble(value, context); +} + +void DoubleUpDownCounter::Add(double value, const opentelemetry::context::Context &context) noexcept +{ + return storage_->RecordDouble(value, context); +} + +LongHistogram::LongHistogram(InstrumentDescriptor instrument_descriptor, + std::unique_ptr storage) + : Synchronous(instrument_descriptor, std::move(storage)) +{} + +void LongHistogram::Record(long value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept +{ + if (value < 0) + { + OTEL_INTERNAL_LOG_WARN( + "[LongHistogram::Record(value, attributes)] negative value provided to histogram Name:" + << instrument_descriptor_.name_ << " Value:" << value); + return; + } + return storage_->RecordLong(value, attributes, context); +} + +void LongHistogram::Record(long value, const opentelemetry::context::Context &context) noexcept +{ + if (value < 0) + { + OTEL_INTERNAL_LOG_WARN( + "[LongHistogram::Record(value)] negative value provided to histogram Name:" + << instrument_descriptor_.name_ << " Value:" << value); + return; + } + return storage_->RecordLong(value, context); +} + +DoubleHistogram::DoubleHistogram(InstrumentDescriptor instrument_descriptor, + std::unique_ptr storage) + : Synchronous(instrument_descriptor, std::move(storage)) +{} + +void DoubleHistogram::Record(double value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept +{ + if (value < 0 || std::isnan(value) || std::isinf(value)) + { + OTEL_INTERNAL_LOG_WARN( + "[DoubleHistogram::Record(value, attributes)] negative/nan/infinite value provided to " + "histogram Name:" + << instrument_descriptor_.name_); + return; + } + return storage_->RecordDouble(value, attributes, context); +} + +void DoubleHistogram::Record(double value, const opentelemetry::context::Context &context) noexcept +{ + if (value < 0 || std::isnan(value) || std::isinf(value)) + { + OTEL_INTERNAL_LOG_WARN( + "[DoubleHistogram::Record(value)] negative/nan/infinite value provided to histogram Name:" + << instrument_descriptor_.name_); + return; + } + return storage_->RecordDouble(value, context); +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/resource/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/src/resource/BUILD new file mode 100644 index 000000000..e42948b7e --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/resource/BUILD @@ -0,0 +1,26 @@ +# Copyright 2020, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "resource", + srcs = glob(["**/*.cc"]), + hdrs = glob(["**/*.h"]), + include_prefix = "src/resource", + deps = [ + "//api", + "//sdk:headers", + ], +) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/resource/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/src/resource/CMakeLists.txt new file mode 100644 index 000000000..f8e8267dc --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/resource/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library(opentelemetry_resources resource.cc resource_detector.cc) + +set_target_properties(opentelemetry_resources PROPERTIES EXPORT_NAME resources) + +target_link_libraries(opentelemetry_resources opentelemetry_common) + +target_include_directories( + opentelemetry_resources + PUBLIC "$") + +install( + TARGETS opentelemetry_resources + EXPORT "${PROJECT_NAME}-target" + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/resource/resource.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/resource/resource.cc new file mode 100644 index 000000000..ae71aefeb --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/resource/resource.cc @@ -0,0 +1,82 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/sdk/resource/experimental_semantic_conventions.h" +#include "opentelemetry/sdk/resource/resource_detector.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace resource +{ + +const std::string kTelemetrySdkLanguage = "telemetry.sdk.language"; +const std::string kTelemetrySdkName = "telemetry.sdk.name"; +const std::string kTelemetrySdkVersion = "telemetry.sdk.version"; +const std::string kServiceName = "service.name"; +const std::string kProcessExecutableName = "process.executable.name"; + +Resource::Resource(const ResourceAttributes &attributes, const std::string &schema_url) noexcept + : attributes_(attributes), schema_url_(schema_url) +{} + +Resource Resource::Merge(const Resource &other) noexcept +{ + ResourceAttributes merged_resource_attributes(other.attributes_); + merged_resource_attributes.insert(attributes_.begin(), attributes_.end()); + return Resource(merged_resource_attributes, other.schema_url_); +} + +Resource Resource::Create(const ResourceAttributes &attributes, const std::string &schema_url) +{ + static auto otel_resource = OTELResourceDetector().Detect(); + auto resource = + Resource::GetDefault().Merge(otel_resource).Merge(Resource{attributes, schema_url}); + + if (resource.attributes_.find(OTEL_GET_RESOURCE_ATTR(AttrServiceName)) == + resource.attributes_.end()) + { + std::string default_service_name = "unknown_service"; + auto it_process_executable_name = + resource.attributes_.find(OTEL_GET_RESOURCE_ATTR(AttrProcessExecutableName)); + if (it_process_executable_name != resource.attributes_.end()) + { + default_service_name += ":" + nostd::get(it_process_executable_name->second); + } + resource.attributes_[OTEL_GET_RESOURCE_ATTR(AttrServiceName)] = default_service_name; + } + return resource; +} + +Resource &Resource::GetEmpty() +{ + static Resource empty_resource; + return empty_resource; +} + +Resource &Resource::GetDefault() +{ + static Resource default_resource( + {{OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkLanguage), "cpp"}, + {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkName), "opentelemetry"}, + {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkVersion), OPENTELEMETRY_SDK_VERSION}}, + std::string{}); + return default_resource; +} + +const ResourceAttributes &Resource::GetAttributes() const noexcept +{ + return attributes_; +} + +const std::string &Resource::GetSchemaURL() const noexcept +{ + return schema_url_; +} + +} // namespace resource +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/resource/resource_detector.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/resource/resource_detector.cc new file mode 100644 index 000000000..c60056539 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/resource/resource_detector.cc @@ -0,0 +1,39 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/resource/resource_detector.h" +#include "opentelemetry/sdk/common/env_variables.h" +#include "opentelemetry/sdk/resource/resource.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace resource +{ + +const char *OTEL_RESOURCE_ATTRIBUTES = "OTEL_RESOURCE_ATTRIBUTES"; + +Resource OTELResourceDetector::Detect() noexcept +{ + auto attributes_str = + opentelemetry::sdk::common::GetEnvironmentVariable(OTEL_RESOURCE_ATTRIBUTES); + if (attributes_str.size() == 0) + { + return Resource(); + } + ResourceAttributes attributes; + std::istringstream iss(attributes_str); + std::string token; + while (std::getline(iss, token, ',')) + { + size_t pos = token.find('='); + std::string key = token.substr(0, pos); + std::string value = token.substr(pos + 1); + attributes[key] = value; + } + return Resource(attributes); +} + +} // namespace resource +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/BUILD new file mode 100644 index 000000000..362680b74 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/BUILD @@ -0,0 +1,29 @@ +# Copyright 2020, OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "trace", + srcs = glob(["**/*.cc"]), + hdrs = glob(["**/*.h"]), + include_prefix = "src/trace", + deps = [ + "//api", + "//sdk:headers", + "//sdk/src/common:global_log_handler", + "//sdk/src/common:random", + "//sdk/src/resource", + ], +) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/CMakeLists.txt new file mode 100644 index 000000000..ddef00fb4 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/CMakeLists.txt @@ -0,0 +1,26 @@ +add_library( + opentelemetry_trace + tracer_context.cc + tracer_provider.cc + tracer.cc + span.cc + batch_span_processor.cc + samplers/parent.cc + samplers/trace_id_ratio.cc + random_id_generator.cc) + +set_target_properties(opentelemetry_trace PROPERTIES EXPORT_NAME trace) + +target_link_libraries(opentelemetry_trace PUBLIC opentelemetry_common + opentelemetry_resources) + +target_include_directories( + opentelemetry_trace + PUBLIC "$") + +install( + TARGETS opentelemetry_trace + EXPORT "${PROJECT_NAME}-target" + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/batch_span_processor.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/batch_span_processor.cc new file mode 100644 index 000000000..0ab042b9a --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/batch_span_processor.cc @@ -0,0 +1,211 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/trace/batch_span_processor.h" + +#include +using opentelemetry::sdk::common::AtomicUniquePtr; +using opentelemetry::sdk::common::CircularBuffer; +using opentelemetry::sdk::common::CircularBufferRange; +using opentelemetry::trace::SpanContext; + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +BatchSpanProcessor::BatchSpanProcessor(std::unique_ptr &&exporter, + const BatchSpanProcessorOptions &options) + : exporter_(std::move(exporter)), + max_queue_size_(options.max_queue_size), + schedule_delay_millis_(options.schedule_delay_millis), + max_export_batch_size_(options.max_export_batch_size), + buffer_(max_queue_size_), + worker_thread_(&BatchSpanProcessor::DoBackgroundWork, this) +{} + +std::unique_ptr BatchSpanProcessor::MakeRecordable() noexcept +{ + return exporter_->MakeRecordable(); +} + +void BatchSpanProcessor::OnStart(Recordable &, const SpanContext &) noexcept +{ + // no-op +} + +void BatchSpanProcessor::OnEnd(std::unique_ptr &&span) noexcept +{ + if (is_shutdown_.load() == true) + { + return; + } + + if (buffer_.Add(span) == false) + { + return; + } + + // If the queue gets at least half full a preemptive notification is + // sent to the worker thread to start a new export cycle. + if (buffer_.size() >= max_queue_size_ / 2) + { + // signal the worker thread + cv_.notify_one(); + } +} + +bool BatchSpanProcessor::ForceFlush(std::chrono::microseconds timeout) noexcept +{ + if (is_shutdown_.load() == true) + { + return false; + } + + is_force_flush_ = true; + + // Keep attempting to wake up the worker thread + while (is_force_flush_.load() == true) + { + cv_.notify_one(); + } + + // Now wait for the worker thread to signal back from the Export method + std::unique_lock lk(force_flush_cv_m_); + while (is_force_flush_notified_.load() == false) + { + force_flush_cv_.wait(lk); + } + + // Notify the worker thread + is_force_flush_notified_ = false; + + return true; +} + +void BatchSpanProcessor::DoBackgroundWork() +{ + auto timeout = schedule_delay_millis_; + + while (true) + { + // Wait for `timeout` milliseconds + std::unique_lock lk(cv_m_); + cv_.wait_for(lk, timeout); + + if (is_shutdown_.load() == true) + { + DrainQueue(); + return; + } + + bool was_force_flush_called = is_force_flush_.load(); + + // Check if this export was the result of a force flush. + if (was_force_flush_called == true) + { + // Since this export was the result of a force flush, signal the + // main thread that the worker thread has been notified + is_force_flush_ = false; + } + else + { + // If the buffer was empty during the entire `timeout` time interval, + // go back to waiting. If this was a spurious wake-up, we export only if + // `buffer_` is not empty. This is acceptable because batching is a best + // mechanism effort here. + if (buffer_.empty() == true) + { + timeout = schedule_delay_millis_; + continue; + } + } + + auto start = std::chrono::steady_clock::now(); + Export(was_force_flush_called); + auto end = std::chrono::steady_clock::now(); + auto duration = std::chrono::duration_cast(end - start); + + // Subtract the duration of this export call from the next `timeout`. + timeout = schedule_delay_millis_ - duration; + } +} + +void BatchSpanProcessor::Export(const bool was_force_flush_called) +{ + std::vector> spans_arr; + + size_t num_spans_to_export; + + if (was_force_flush_called == true) + { + num_spans_to_export = buffer_.size(); + } + else + { + num_spans_to_export = + buffer_.size() >= max_export_batch_size_ ? max_export_batch_size_ : buffer_.size(); + } + + buffer_.Consume(num_spans_to_export, + [&](CircularBufferRange> range) noexcept { + range.ForEach([&](AtomicUniquePtr &ptr) { + std::unique_ptr swap_ptr = std::unique_ptr(nullptr); + ptr.Swap(swap_ptr); + spans_arr.push_back(std::unique_ptr(swap_ptr.release())); + return true; + }); + }); + + exporter_->Export(nostd::span>(spans_arr.data(), spans_arr.size())); + + // Notify the main thread in case this export was the result of a force flush. + if (was_force_flush_called == true) + { + is_force_flush_notified_ = true; + while (is_force_flush_notified_.load() == true) + { + force_flush_cv_.notify_one(); + } + } +} + +void BatchSpanProcessor::DrainQueue() +{ + while (buffer_.empty() == false) + { + Export(false); + } +} + +bool BatchSpanProcessor::Shutdown(std::chrono::microseconds timeout) noexcept +{ + std::lock_guard shutdown_guard{shutdown_m_}; + bool already_shutdown = is_shutdown_.exchange(true); + + if (worker_thread_.joinable()) + { + cv_.notify_one(); + worker_thread_.join(); + } + + // Should only shutdown exporter ONCE. + if (!already_shutdown && exporter_ != nullptr) + { + return exporter_->Shutdown(); + } + + return true; +} + +BatchSpanProcessor::~BatchSpanProcessor() +{ + if (is_shutdown_.load() == false) + { + Shutdown(); + } +} + +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/random_id_generator.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/random_id_generator.cc new file mode 100644 index 000000000..e2fa5b098 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/random_id_generator.cc @@ -0,0 +1,30 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/trace/random_id_generator.h" +#include "opentelemetry/version.h" +#include "src/common/random.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +namespace trace_api = opentelemetry::trace; + +trace_api::SpanId RandomIdGenerator::GenerateSpanId() noexcept +{ + uint8_t span_id_buf[trace_api::SpanId::kSize]; + sdk::common::Random::GenerateRandomBuffer(span_id_buf); + return trace_api::SpanId(span_id_buf); +} + +trace_api::TraceId RandomIdGenerator::GenerateTraceId() noexcept +{ + uint8_t trace_id_buf[trace_api::TraceId::kSize]; + sdk::common::Random::GenerateRandomBuffer(trace_id_buf); + return trace_api::TraceId(trace_id_buf); +} +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/samplers/parent.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/samplers/parent.cc new file mode 100644 index 000000000..3aa6b4a45 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/samplers/parent.cc @@ -0,0 +1,48 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/trace/samplers/parent.h" + +namespace trace_api = opentelemetry::trace; + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +ParentBasedSampler::ParentBasedSampler(std::shared_ptr delegate_sampler) noexcept + : delegate_sampler_(delegate_sampler), + description_("ParentBased{" + std::string{delegate_sampler->GetDescription()} + "}") +{} + +SamplingResult ParentBasedSampler::ShouldSample( + const trace_api::SpanContext &parent_context, + trace_api::TraceId trace_id, + nostd::string_view name, + trace_api::SpanKind span_kind, + const opentelemetry::common::KeyValueIterable &attributes, + const trace_api::SpanContextKeyValueIterable &links) noexcept +{ + if (!parent_context.IsValid()) + { + // If no parent (root span) exists returns the result of the delegateSampler + return delegate_sampler_->ShouldSample(parent_context, trace_id, name, span_kind, attributes, + links); + } + + // If parent exists: + if (parent_context.IsSampled()) + { + return {Decision::RECORD_AND_SAMPLE, nullptr, parent_context.trace_state()}; + } + + return {Decision::DROP, nullptr, parent_context.trace_state()}; +} + +nostd::string_view ParentBasedSampler::GetDescription() const noexcept +{ + return description_; +} +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/samplers/trace_id_ratio.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/samplers/trace_id_ratio.cc new file mode 100644 index 000000000..7c154346d --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/samplers/trace_id_ratio.cc @@ -0,0 +1,111 @@ +// Copyright 2020, Open Telemetry Authors +// Copyright 2017, OpenCensus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "opentelemetry/sdk/trace/samplers/trace_id_ratio.h" + +#include +#include +#include + +namespace trace_api = opentelemetry::trace; + +namespace +{ +/** + * Converts a ratio in [0, 1] to a threshold in [0, UINT64_MAX] + * + * @param ratio a required value top be converted to uint64_t. is + * bounded by 1 >= ratio >= 0. + * @return Returns threshold value computed after converting ratio to + * uint64_t datatype + */ +uint64_t CalculateThreshold(double ratio) noexcept +{ + if (ratio <= 0.0) + return 0; + if (ratio >= 1.0) + return UINT64_MAX; + + // We can't directly return ratio * UINT64_MAX. + // + // UINT64_MAX is (2^64)-1, but as a double rounds up to 2^64. + // For probabilities >= 1-(2^-54), the product wraps to zero! + // Instead, calculate the high and low 32 bits separately. + const double product = UINT32_MAX * ratio; + double hi_bits, lo_bits = ldexp(modf(product, &hi_bits), 32) + product; + return (static_cast(hi_bits) << 32) + static_cast(lo_bits); +} + +/** + * @param trace_id a required value to be converted to uint64_t. trace_id must + * at least 8 bytes long + * @return Returns threshold value computed after converting trace_id to + * uint64_t datatype + */ +uint64_t CalculateThresholdFromBuffer(const trace_api::TraceId &trace_id) noexcept +{ + // We only use the first 8 bytes of TraceId. + static_assert(trace_api::TraceId::kSize >= 8, "TraceID must be at least 8 bytes long."); + + uint64_t res = 0; + std::memcpy(&res, &trace_id, 8); + + double ratio = (double)res / UINT64_MAX; + + return CalculateThreshold(ratio); +} +} // namespace + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +TraceIdRatioBasedSampler::TraceIdRatioBasedSampler(double ratio) + : threshold_(CalculateThreshold(ratio)) +{ + if (ratio > 1.0) + ratio = 1.0; + if (ratio < 0.0) + ratio = 0.0; + description_ = "TraceIdRatioBasedSampler{" + std::to_string(ratio) + "}"; +} + +SamplingResult TraceIdRatioBasedSampler::ShouldSample( + const trace_api::SpanContext & /*parent_context*/, + trace_api::TraceId trace_id, + nostd::string_view /*name*/, + trace_api::SpanKind /*span_kind*/, + const opentelemetry::common::KeyValueIterable & /*attributes*/, + const trace_api::SpanContextKeyValueIterable & /*links*/) noexcept +{ + if (threshold_ == 0) + return {Decision::DROP, nullptr}; + + if (CalculateThresholdFromBuffer(trace_id) <= threshold_) + { + return {Decision::RECORD_AND_SAMPLE, nullptr}; + } + + return {Decision::DROP, nullptr}; +} + +nostd::string_view TraceIdRatioBasedSampler::GetDescription() const noexcept +{ + return description_; +} +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/span.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/span.cc new file mode 100644 index 000000000..13ea99126 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/span.cc @@ -0,0 +1,187 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "src/trace/span.h" +#include "src/common/random.h" + +#include "opentelemetry/context/runtime_context.h" +#include "opentelemetry/trace/trace_flags.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ + +using opentelemetry::common::SteadyTimestamp; +using opentelemetry::common::SystemTimestamp; +namespace common = opentelemetry::common; + +namespace +{ +SystemTimestamp NowOr(const SystemTimestamp &system) +{ + if (system == SystemTimestamp()) + { + return SystemTimestamp(std::chrono::system_clock::now()); + } + else + { + return system; + } +} + +SteadyTimestamp NowOr(const SteadyTimestamp &steady) +{ + if (steady == SteadyTimestamp()) + { + return SteadyTimestamp(std::chrono::steady_clock::now()); + } + else + { + return steady; + } +} +} // namespace + +Span::Span(std::shared_ptr &&tracer, + nostd::string_view name, + const common::KeyValueIterable &attributes, + const trace_api::SpanContextKeyValueIterable &links, + const trace_api::StartSpanOptions &options, + const trace_api::SpanContext &parent_span_context, + std::unique_ptr span_context) noexcept + : tracer_{std::move(tracer)}, + recordable_{tracer_->GetProcessor().MakeRecordable()}, + start_steady_time{options.start_steady_time}, + span_context_(std::move(span_context)), + has_ended_{false} +{ + if (recordable_ == nullptr) + { + return; + } + recordable_->SetName(name); + recordable_->SetInstrumentationLibrary(tracer_->GetInstrumentationLibrary()); + recordable_->SetIdentity(*span_context_, parent_span_context.IsValid() + ? parent_span_context.span_id() + : trace_api::SpanId()); + + attributes.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept { + recordable_->SetAttribute(key, value); + return true; + }); + + links.ForEachKeyValue([&](opentelemetry::trace::SpanContext span_context, + const common::KeyValueIterable &attributes) { + recordable_->AddLink(span_context, attributes); + return true; + }); + + recordable_->SetSpanKind(options.kind); + recordable_->SetStartTime(NowOr(options.start_system_time)); + start_steady_time = NowOr(options.start_steady_time); + recordable_->SetResource(tracer_->GetResource()); + tracer_->GetProcessor().OnStart(*recordable_, parent_span_context); +} + +Span::~Span() +{ + End(); +} + +void Span::SetAttribute(nostd::string_view key, const common::AttributeValue &value) noexcept +{ + std::lock_guard lock_guard{mu_}; + if (recordable_ == nullptr) + { + return; + } + + recordable_->SetAttribute(key, value); +} + +void Span::AddEvent(nostd::string_view name) noexcept +{ + std::lock_guard lock_guard{mu_}; + if (recordable_ == nullptr) + { + return; + } + recordable_->AddEvent(name); +} + +void Span::AddEvent(nostd::string_view name, SystemTimestamp timestamp) noexcept +{ + std::lock_guard lock_guard{mu_}; + if (recordable_ == nullptr) + { + return; + } + recordable_->AddEvent(name, timestamp); +} + +void Span::AddEvent(nostd::string_view name, + SystemTimestamp timestamp, + const common::KeyValueIterable &attributes) noexcept +{ + std::lock_guard lock_guard{mu_}; + if (recordable_ == nullptr) + { + return; + } + recordable_->AddEvent(name, timestamp, attributes); +} + +void Span::SetStatus(opentelemetry::trace::StatusCode code, nostd::string_view description) noexcept +{ + std::lock_guard lock_guard{mu_}; + if (recordable_ == nullptr) + { + return; + } + recordable_->SetStatus(code, description); +} + +void Span::UpdateName(nostd::string_view name) noexcept +{ + std::lock_guard lock_guard{mu_}; + if (recordable_ == nullptr) + { + return; + } + recordable_->SetName(name); +} + +void Span::End(const trace_api::EndSpanOptions &options) noexcept +{ + std::lock_guard lock_guard{mu_}; + + if (has_ended_ == true) + { + return; + } + has_ended_ = true; + + if (recordable_ == nullptr) + { + return; + } + + auto end_steady_time = NowOr(options.end_steady_time); + recordable_->SetDuration(std::chrono::steady_clock::time_point(end_steady_time) - + std::chrono::steady_clock::time_point(start_steady_time)); + + tracer_->GetProcessor().OnEnd(std::move(recordable_)); + recordable_.reset(); +} + +bool Span::IsRecording() const noexcept +{ + std::lock_guard lock_guard{mu_}; + return recordable_ != nullptr; +} +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/span.h b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/span.h new file mode 100644 index 000000000..f507afbef --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/span.h @@ -0,0 +1,66 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +#include "opentelemetry/sdk/trace/tracer.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +class Span final : public opentelemetry::trace::Span +{ +public: + Span(std::shared_ptr &&tracer, + nostd::string_view name, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::trace::SpanContextKeyValueIterable &links, + const opentelemetry::trace::StartSpanOptions &options, + const opentelemetry::trace::SpanContext &parent_span_context, + std::unique_ptr span_context) noexcept; + + ~Span() override; + + // trace::Span + void SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept override; + + void AddEvent(nostd::string_view name) noexcept override; + + void AddEvent(nostd::string_view name, + opentelemetry::common::SystemTimestamp timestamp) noexcept override; + + void AddEvent(nostd::string_view name, + opentelemetry::common::SystemTimestamp timestamp, + const opentelemetry::common::KeyValueIterable &attributes) noexcept override; + + void SetStatus(opentelemetry::trace::StatusCode code, + nostd::string_view description) noexcept override; + + void UpdateName(nostd::string_view name) noexcept override; + + void End(const opentelemetry::trace::EndSpanOptions &options = {}) noexcept override; + + bool IsRecording() const noexcept override; + + opentelemetry::trace::SpanContext GetContext() const noexcept override + { + return *span_context_.get(); + } + +private: + std::shared_ptr tracer_; + mutable std::mutex mu_; + std::unique_ptr recordable_; + opentelemetry::common::SteadyTimestamp start_steady_time; + std::unique_ptr span_context_; + bool has_ended_; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/tracer.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/tracer.cc new file mode 100644 index 000000000..be5e5f7d9 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/tracer.cc @@ -0,0 +1,116 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/trace/tracer.h" +#include "opentelemetry/context/runtime_context.h" +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/sdk/common/atomic_shared_ptr.h" +#include "opentelemetry/trace/context.h" +#include "opentelemetry/version.h" +#include "src/trace/span.h" + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ + +Tracer::Tracer(std::shared_ptr context, + std::unique_ptr instrumentation_library) noexcept + : instrumentation_library_{std::move(instrumentation_library)}, context_{context} +{} + +nostd::shared_ptr Tracer::StartSpan( + nostd::string_view name, + const opentelemetry::common::KeyValueIterable &attributes, + const trace_api::SpanContextKeyValueIterable &links, + const trace_api::StartSpanOptions &options) noexcept +{ + trace_api::SpanContext parent_context = GetCurrentSpan()->GetContext(); + if (nostd::holds_alternative(options.parent)) + { + auto span_context = nostd::get(options.parent); + if (span_context.IsValid()) + { + parent_context = span_context; + } + } + else if (nostd::holds_alternative(options.parent)) + { + auto context = nostd::get(options.parent); + // fetch span context from parent span stored in the context + auto span_context = opentelemetry::trace::GetSpan(context)->GetContext(); + if (span_context.IsValid()) + { + parent_context = span_context; + } + } + + trace_api::TraceId trace_id; + trace_api::SpanId span_id = GetIdGenerator().GenerateSpanId(); + bool is_parent_span_valid = false; + + if (parent_context.IsValid()) + { + trace_id = parent_context.trace_id(); + is_parent_span_valid = true; + } + else + { + trace_id = GetIdGenerator().GenerateTraceId(); + } + + auto sampling_result = context_->GetSampler().ShouldSample(parent_context, trace_id, name, + options.kind, attributes, links); + auto trace_flags = sampling_result.decision == Decision::DROP + ? trace_api::TraceFlags{} + : trace_api::TraceFlags{trace_api::TraceFlags::kIsSampled}; + + auto span_context = std::unique_ptr(new trace_api::SpanContext( + trace_id, span_id, trace_flags, false, + sampling_result.trace_state ? sampling_result.trace_state + : is_parent_span_valid ? parent_context.trace_state() + : trace_api::TraceState::GetDefault())); + + if (sampling_result.decision == Decision::DROP) + { + // create no-op span with valid span-context. + + auto noop_span = nostd::shared_ptr{ + new (std::nothrow) trace_api::NoopSpan(this->shared_from_this(), std::move(span_context))}; + return noop_span; + } + else + { + + auto span = nostd::shared_ptr{ + new (std::nothrow) Span{this->shared_from_this(), name, attributes, links, options, + parent_context, std::move(span_context)}}; + + // if the attributes is not nullptr, add attributes to the span. + if (sampling_result.attributes) + { + for (auto &kv : *sampling_result.attributes) + { + span->SetAttribute(kv.first, kv.second); + } + } + + return span; + } +} + +void Tracer::ForceFlushWithMicroseconds(uint64_t timeout) noexcept +{ + (void)timeout; +} + +void Tracer::CloseWithMicroseconds(uint64_t timeout) noexcept +{ + (void)timeout; +} +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/tracer_context.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/tracer_context.cc new file mode 100644 index 000000000..d20421802 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/tracer_context.cc @@ -0,0 +1,63 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/trace/tracer_context.h" +#include "opentelemetry/sdk/trace/multi_span_processor.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +namespace resource = opentelemetry::sdk::resource; + +TracerContext::TracerContext(std::vector> &&processors, + resource::Resource resource, + std::unique_ptr sampler, + std::unique_ptr id_generator) noexcept + : resource_(resource), + sampler_(std::move(sampler)), + id_generator_(std::move(id_generator)), + processor_(std::unique_ptr(new MultiSpanProcessor(std::move(processors)))) +{} + +Sampler &TracerContext::GetSampler() const noexcept +{ + return *sampler_; +} + +const resource::Resource &TracerContext::GetResource() const noexcept +{ + return resource_; +} + +opentelemetry::sdk::trace::IdGenerator &TracerContext::GetIdGenerator() const noexcept +{ + return *id_generator_; +} + +void TracerContext::AddProcessor(std::unique_ptr processor) noexcept +{ + + auto multi_processor = static_cast(processor_.get()); + multi_processor->AddProcessor(std::move(processor)); +} + +SpanProcessor &TracerContext::GetProcessor() const noexcept +{ + return *processor_; +} + +bool TracerContext::ForceFlush(std::chrono::microseconds timeout) noexcept +{ + return processor_->ForceFlush(timeout); +} + +bool TracerContext::Shutdown() noexcept +{ + return processor_->Shutdown(); +} + +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/tracer_provider.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/tracer_provider.cc new file mode 100644 index 000000000..64997d7d0 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/trace/tracer_provider.cc @@ -0,0 +1,104 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/trace/tracer_provider.h" +#include "opentelemetry/sdk_config.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +namespace resource = opentelemetry::sdk::resource; +namespace trace_api = opentelemetry::trace; + +TracerProvider::TracerProvider(std::shared_ptr context) noexcept + : context_{context} +{} + +TracerProvider::TracerProvider(std::unique_ptr processor, + resource::Resource resource, + std::unique_ptr sampler, + std::unique_ptr id_generator) noexcept +{ + std::vector> processors; + processors.push_back(std::move(processor)); + context_ = std::make_shared(std::move(processors), resource, std::move(sampler), + std::move(id_generator)); +} + +TracerProvider::TracerProvider(std::vector> &&processors, + resource::Resource resource, + std::unique_ptr sampler, + std::unique_ptr id_generator) noexcept +{ + context_ = std::make_shared(std::move(processors), resource, std::move(sampler), + std::move(id_generator)); +} + +TracerProvider::~TracerProvider() +{ + // Tracer hold the shared pointer to the context. So we can not use destructor of TracerContext to + // Shutdown and flush all pending recordables when we have more than one tracers.These recordables + // may use the raw pointer of instrumentation_library_ in Tracer + if (context_) + { + context_->Shutdown(); + } +} + +nostd::shared_ptr TracerProvider::GetTracer( + nostd::string_view library_name, + nostd::string_view library_version, + nostd::string_view schema_url) noexcept +{ + if (library_name.data() == nullptr) + { + OTEL_INTERNAL_LOG_ERROR("[TracerProvider::GetTracer] Library name is null."); + library_name = ""; + } + else if (library_name == "") + { + OTEL_INTERNAL_LOG_ERROR("[TracerProvider::GetTracer] Library name is empty."); + } + + const std::lock_guard guard(lock_); + + for (auto &tracer : tracers_) + { + auto &tracer_lib = tracer->GetInstrumentationLibrary(); + if (tracer_lib.equal(library_name, library_version, schema_url)) + { + return nostd::shared_ptr{tracer}; + } + } + + auto lib = InstrumentationLibrary::Create(library_name, library_version, schema_url); + tracers_.push_back(std::shared_ptr( + new sdk::trace::Tracer(context_, std::move(lib)))); + return nostd::shared_ptr{tracers_.back()}; +} + +void TracerProvider::AddProcessor(std::unique_ptr processor) noexcept +{ + context_->AddProcessor(std::move(processor)); +} + +const resource::Resource &TracerProvider::GetResource() const noexcept +{ + return context_->GetResource(); +} + +bool TracerProvider::Shutdown() noexcept +{ + return context_->Shutdown(); +} + +bool TracerProvider::ForceFlush(std::chrono::microseconds timeout) noexcept +{ + return context_->ForceFlush(timeout); +} + +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/version/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/src/version/CMakeLists.txt new file mode 100644 index 000000000..59231070b --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/version/CMakeLists.txt @@ -0,0 +1,17 @@ +add_library(opentelemetry_version version.cc) + +set_target_properties(opentelemetry_version PROPERTIES EXPORT_NAME version) + +target_link_libraries(opentelemetry_version PUBLIC opentelemetry_api + opentelemetry_sdk) + +target_include_directories( + opentelemetry_version + PUBLIC "$") + +install( + TARGETS opentelemetry_version + EXPORT "${PROJECT_NAME}-target" + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/src/version/version.cc b/src/jaegertracing/opentelemetry-cpp/sdk/src/version/version.cc new file mode 100644 index 000000000..9303a305a --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/src/version/version.cc @@ -0,0 +1,24 @@ +// Please DONOT touch this file. +// Any changes done here would be overwritten during release/build. + +#include "opentelemetry/sdk/version/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace version +{ +const int MAJOR_VERSION = 1; +const int MINOR_VERSION = 4; +const int PATCH_VERSION = 0; +const char *PRE_RELEASE = ""; +const char *BUILD_METADATA = ""; +const int COUNT_NEW_COMMITS = 0; +const char *BRANCH = ""; +const char *COMMIT_HASH = ""; +const char *SHORT_VERSION = ""; +const char *FULL_VERSION = ""; +const char *BUILD_DATE = ""; +} // namespace version +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/CMakeLists.txt new file mode 100644 index 000000000..dc8cc2b22 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/CMakeLists.txt @@ -0,0 +1,12 @@ +add_subdirectory(common) +add_subdirectory(trace) +if(WITH_METRICS_PREVIEW) + add_subdirectory(_metrics) +else() + add_subdirectory(metrics) +endif() +if(WITH_LOGS_PREVIEW) + add_subdirectory(logs) +endif() +add_subdirectory(resource) +add_subdirectory(instrumentationlibrary) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/BUILD new file mode 100644 index 000000000..44835d102 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/BUILD @@ -0,0 +1,165 @@ +cc_test( + name = "controller_test", + srcs = [ + "controller_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//exporters/ostream:ostream_metrics_exporter_deprecated", + "//sdk/src/_metrics:metrics_deprecated", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "gauge_aggregator_test", + srcs = [ + "gauge_aggregator_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/_metrics:metrics_deprecated", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "min_max_sum_count_aggregator_test", + srcs = [ + "min_max_sum_count_aggregator_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/_metrics:metrics_deprecated", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "meter_provider_sdk_test", + srcs = [ + "meter_provider_sdk_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/_metrics:metrics_deprecated", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "meter_test", + srcs = [ + "meter_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/_metrics:metrics_deprecated", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "counter_aggregator_test", + srcs = [ + "counter_aggregator_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/_metrics:metrics_deprecated", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "exact_aggregator_test", + srcs = [ + "exact_aggregator_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/_metrics:metrics_deprecated", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "histogram_aggregator_test", + srcs = [ + "histogram_aggregator_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/_metrics:metrics_deprecated", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "metric_instrument_test", + srcs = [ + "metric_instrument_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/_metrics:metrics_deprecated", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "sketch_aggregator_test", + srcs = [ + "sketch_aggregator_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/_metrics:metrics_deprecated", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "ungrouped_processor_test", + srcs = [ + "ungrouped_processor_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/_metrics:metrics_deprecated", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/CMakeLists.txt new file mode 100644 index 000000000..5d950ffb3 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/CMakeLists.txt @@ -0,0 +1,21 @@ +foreach( + testname + meter_provider_sdk_test + gauge_aggregator_test + min_max_sum_count_aggregator_test + exact_aggregator_test + counter_aggregator_test + histogram_aggregator_test + ungrouped_processor_test + meter_test + metric_instrument_test + controller_test) + add_executable(${testname} "${testname}.cc") + target_link_libraries( + ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} + opentelemetry_metrics_deprecated) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX metrics. + TEST_LIST ${testname}) +endforeach() diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/controller_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/controller_test.cc new file mode 100644 index 000000000..aa92c8d04 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/controller_test.cc @@ -0,0 +1,56 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/_metrics/controller.h" +# include "opentelemetry/sdk/_metrics/meter.h" +# include "opentelemetry/sdk/_metrics/ungrouped_processor.h" + +# include +# include +# include +// #include + +namespace metrics_api = opentelemetry::metrics; +using namespace opentelemetry::sdk::common; + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +class DummyExporter : public MetricsExporter +{ + ExportResult Export(const std::vector &records) noexcept + { + return ExportResult::kSuccess; + } +}; + +TEST(Controller, Constructor) +{ + + std::shared_ptr meter = + std::shared_ptr(new Meter("Test")); + PushController alpha(meter, std::unique_ptr(new DummyExporter), + std::shared_ptr( + new opentelemetry::sdk::metrics::UngroupedMetricsProcessor(false)), + .05); + + auto instr = meter->NewIntCounter("test", "none", "none", true); + std::map labels = {{"key", "value"}}; + auto labelkv = opentelemetry::common::KeyValueIterableView{labels}; + + alpha.start(); + + for (int i = 0; i < 20; i++) + { + instr->add(i, labelkv); + } + alpha.stop(); +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/counter_aggregator_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/counter_aggregator_test.cc new file mode 100644 index 000000000..7ef452e77 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/counter_aggregator_test.cc @@ -0,0 +1,120 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/_metrics/aggregator/counter_aggregator.h" + +# include +# include +# include + +namespace metrics_api = opentelemetry::metrics; + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +TEST(CounterAggregator, NoUpdates) +{ + CounterAggregator alpha(metrics_api::InstrumentKind::Counter); + + EXPECT_EQ(alpha.get_checkpoint().size(), 1); + EXPECT_EQ(alpha.get_checkpoint()[0], 0); + + alpha.checkpoint(); + EXPECT_EQ(alpha.get_checkpoint().size(), 1); + EXPECT_EQ(alpha.get_checkpoint()[0], 0); +} + +TEST(CounterAggregator, Update) +{ + CounterAggregator alpha(metrics_api::InstrumentKind::Counter); + CounterAggregator beta(metrics_api::InstrumentKind::Counter); + + for (int i = 0; i < 123456; i++) + { + alpha.update(1); + } + + int sum = 0; + for (int i = 0; i < 100; i++) + { + int tmp = std::rand() % 128; + beta.update(tmp); + sum += tmp; + } + + EXPECT_EQ(alpha.get_checkpoint()[0], 0); // checkpoint shouldn't change even with updates + EXPECT_EQ(beta.get_checkpoint()[0], 0); + + alpha.checkpoint(); + beta.checkpoint(); + + EXPECT_EQ(alpha.get_checkpoint()[0], 123456); + EXPECT_EQ(beta.get_checkpoint()[0], sum); + + alpha.update(15); + alpha.checkpoint(); + EXPECT_EQ(alpha.get_checkpoint()[0], 15); // reset to 0 after first checkpoint call +} + +// callback update function used to test concurrent calls +void incrementingCallback(Aggregator &agg) +{ + for (int i = 0; i < 2000000; i++) + { + agg.update(1); + } +} + +TEST(CounterAggregator, Concurrency) +{ + CounterAggregator alpha(metrics_api::InstrumentKind::Counter); + + // spawn new threads that initiate the callback + std::thread first(incrementingCallback, std::ref(alpha)); + std::thread second(incrementingCallback, std::ref(alpha)); + + first.join(); + second.join(); + + alpha.checkpoint(); + + // race conditions result in values below 2*2000000 + EXPECT_EQ(alpha.get_checkpoint()[0], 2 * 2000000); +} + +TEST(CounterAggregator, Merge) +{ + CounterAggregator alpha(metrics_api::InstrumentKind::Counter); + CounterAggregator beta(metrics_api::InstrumentKind::Counter); + + alpha.merge(beta); + + alpha.checkpoint(); + EXPECT_EQ(alpha.get_checkpoint()[0], 0); // merge with no updates + + for (int i = 0; i < 500; i++) + { + alpha.update(1); + } + + for (int i = 0; i < 700; i++) + { + beta.update(1); + } + + alpha.merge(beta); + alpha.checkpoint(); + EXPECT_EQ(alpha.get_checkpoint()[0], 1200); + + // HistogramAggregator gamma(metrics_api::BoundInstrumentKind::BoundValueRecorder); + // ASSERT_THROW(alpha.merge(gamma), AggregatorMismatch); +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/exact_aggregator_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/exact_aggregator_test.cc new file mode 100644 index 000000000..443cb65ea --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/exact_aggregator_test.cc @@ -0,0 +1,229 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include + +#ifdef ENABLE_METRICS_PREVIEW +# include + +# include "opentelemetry/sdk/_metrics/aggregator/exact_aggregator.h" + +using namespace opentelemetry::sdk::metrics; +namespace metrics_api = opentelemetry::metrics; + +TEST(ExactAggregatorOrdered, Update) +{ + ExactAggregator agg(metrics_api::InstrumentKind::Counter); + + std::vector correct; + + ASSERT_EQ(agg.get_values(), correct); + + agg.update(1); + correct.push_back(1); + + ASSERT_EQ(agg.get_values(), std::vector{1}); + + for (int i = 2; i <= 5; ++i) + { + correct.push_back(i); + agg.update(i); + } + ASSERT_EQ(agg.get_values(), correct); +} + +TEST(ExactAggregatorOrdered, Checkpoint) +{ + ExactAggregator agg(metrics_api::InstrumentKind::Counter); + + std::vector correct; + + ASSERT_EQ(agg.get_checkpoint(), correct); + + agg.update(1); + correct.push_back(1); + agg.checkpoint(); + + ASSERT_EQ(agg.get_checkpoint(), correct); +} + +TEST(ExactAggregatorOrdered, Merge) +{ + ExactAggregator agg1(metrics_api::InstrumentKind::Counter); + ExactAggregator agg2(metrics_api::InstrumentKind::Counter); + + agg1.update(1); + agg2.update(2); + agg1.merge(agg2); + + std::vector correct{1, 2}; + + ASSERT_EQ(agg1.get_values(), correct); +} + +TEST(ExactAggregatorOrdered, BadMerge) +{ + // This verifies that we encounter and error when we try to merge + // two aggregators of different numeric types together. + ExactAggregator agg1(metrics_api::InstrumentKind::Counter); + ExactAggregator agg2(metrics_api::InstrumentKind::ValueRecorder); + + agg1.update(1); + agg2.update(2); + + agg1.merge(agg2); + + // Verify that the aggregators did NOT merge + std::vector correct{1}; + ASSERT_EQ(agg1.get_values(), correct); +} + +TEST(ExactAggregatorOrdered, Types) +{ + // This test verifies that we do not encounter any errors when + // using various numeric types. + ExactAggregator agg_int(metrics_api::InstrumentKind::Counter); + ExactAggregator agg_long(metrics_api::InstrumentKind::Counter); + ExactAggregator agg_float(metrics_api::InstrumentKind::Counter); + ExactAggregator agg_double(metrics_api::InstrumentKind::Counter); + + for (int i = 1; i <= 5; ++i) + { + agg_int.update(i); + agg_long.update(i); + } + + for (float i = 1.0; i <= 5.0; i += 1) + { + agg_float.update(i); + agg_double.update(i); + } + + std::vector correct_int{1, 2, 3, 4, 5}; + std::vector correct_long{1, 2, 3, 4, 5}; + std::vector correct_float{1.0, 2.0, 3.0, 4.0, 5.0}; + std::vector correct_double{1.0, 2.0, 3.0, 4.0, 5.0}; + + ASSERT_EQ(agg_int.get_values(), correct_int); + ASSERT_EQ(agg_long.get_values(), correct_long); + ASSERT_EQ(agg_float.get_values(), correct_float); + ASSERT_EQ(agg_double.get_values(), correct_double); +} + +TEST(ExactAggregatorQuant, Update) +{ + ExactAggregator agg(metrics_api::InstrumentKind::Counter, true); + + std::vector correct; + + ASSERT_EQ(agg.get_values(), correct); + + agg.update(1); + correct.push_back(1); + + ASSERT_EQ(agg.get_values(), std::vector{1}); + + for (int i = 2; i <= 5; ++i) + { + correct.push_back(i); + agg.update(i); + } + ASSERT_EQ(agg.get_values(), correct); +} + +TEST(ExactAggregatorQuant, Checkpoint) +{ + // This test verifies that the aggregator updates correctly when + // quantile estimation is turned on. + + ExactAggregator agg(metrics_api::InstrumentKind::Counter, true); + + std::vector correct; + + ASSERT_EQ(agg.get_checkpoint(), correct); + + agg.update(1); + agg.update(0); + agg.update(-1); + + // The vector MUST be sorted when checkpointed + correct.push_back(-1); + correct.push_back(0); + correct.push_back(1); + agg.checkpoint(); + + ASSERT_EQ(agg.get_checkpoint(), correct); +} + +TEST(ExactAggregatorQuant, Quantile) +{ + // This test verifies that the quantile estimation function returns + // the correct values. + + ExactAggregator agg(metrics_api::InstrumentKind::Counter, true); + + std::vector tmp{3, 9, 42, 57, 163, 210, 272, 300}; + for (int i : tmp) + { + agg.update(i); + } + agg.checkpoint(); + ASSERT_EQ(agg.get_quantiles(.25), 42); + ASSERT_EQ(agg.get_quantiles(0.5), 163); + ASSERT_EQ(agg.get_quantiles(0.75), 272); +} + +TEST(ExactAggregatorInOrder, Quantile) +{ + // This test verifies that if the user has an exact aggregator in "in-order" mode + // an exception will be thrown if they call the quantile() function. + ExactAggregator agg(metrics_api::InstrumentKind::Counter); + + std::vector tmp{3, 9, 42, 57, 163, 210, 272, 300}; + for (int i : tmp) + { + agg.update(i); + } + agg.checkpoint(); +# if __EXCEPTIONS + ASSERT_THROW(agg.get_quantiles(0.5), std::domain_error); +# else +# endif +} + +void callback(ExactAggregator &agg) +{ + for (int i = 1; i <= 10000; ++i) + { + agg.update(i); + } +} + +TEST(ExactAggregatorQuant, Concurrency) +{ + // This test checks that the aggregator updates appropriately + // when called in a multi-threaded context. + ExactAggregator agg(metrics_api::InstrumentKind::Counter, true); + + std::thread first(&callback, std::ref(agg)); + std::thread second(&callback, std::ref(agg)); + + first.join(); + second.join(); + + std::vector correct; + for (int i = 1; i <= 10000; ++i) + { + correct.push_back(i); + correct.push_back(i); + } + agg.checkpoint(); + + ASSERT_EQ(agg.get_checkpoint(), correct); +} +#else +TEST(ExactAggregatorQuant, DummyTest) +{ + // empty +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/gauge_aggregator_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/gauge_aggregator_test.cc new file mode 100644 index 000000000..9565e0629 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/gauge_aggregator_test.cc @@ -0,0 +1,133 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_METRICS_PREVIEW +# include +# include + +# include "opentelemetry/sdk/_metrics/aggregator/gauge_aggregator.h" + +using namespace opentelemetry::sdk::metrics; +namespace metrics_api = opentelemetry::metrics; + +TEST(GaugeAggregator, Update) +{ + // This tests that the aggregator updates the maintained value correctly + // after a call to the update() function. + auto agg = new GaugeAggregator(metrics_api::InstrumentKind::Counter); + + // Verify default value + ASSERT_EQ(agg->get_values()[0], 0); + + // Verify that the value updates correctly + agg->update(1); + ASSERT_EQ(agg->get_values()[0], 1); + + // Verify that the value continually updates correctly + for (int i = 0; i < 10; ++i) + { + agg->update(i); + } + ASSERT_EQ(agg->get_values()[0], 9); + delete agg; +} + +TEST(GaugeAggregator, Checkpoint) +{ + // This tests that the aggregator correctly updates the + // checkpoint_ value after a call to update() followed + // by a call to checkpoint(). + GaugeAggregator agg(metrics_api::InstrumentKind::Counter); + + // Verify default checkpoint, before updates + ASSERT_EQ(agg.get_checkpoint()[0], 0); + + agg.update(10); + agg.checkpoint(); + + // Verify that the new checkpoint contains the update value + ASSERT_EQ(agg.get_checkpoint()[0], 10); +} + +TEST(GaugeAggregator, Merge) +{ + // This tests that the values_ vector is updated correctly after + // two aggregators are merged together. + GaugeAggregator agg1(metrics_api::InstrumentKind::Counter); + GaugeAggregator agg2(metrics_api::InstrumentKind::Counter); + + agg1.update(1); + agg2.update(2); + + agg1.merge(agg2); + + // Verify that the aggregators merged and the value was updated correctly + ASSERT_EQ(agg1.get_values()[0], 2); +} + +TEST(GaugeAggregator, BadMerge) +{ + // This verifies that we encounter and error when we try to merge + // two aggregators of different numeric types together. + GaugeAggregator agg1(metrics_api::InstrumentKind::Counter); + GaugeAggregator agg2(metrics_api::InstrumentKind::ValueRecorder); + + agg1.update(1); + agg2.update(2); + agg1.merge(agg2); + + // Verify that the aggregators did NOT merge + std::vector correct{1}; + ASSERT_EQ(agg1.get_values(), correct); +} + +TEST(GaugeAggregator, Types) +{ + // This test verifies that we do not encounter any errors when + // using various numeric types. + GaugeAggregator agg_int(metrics_api::InstrumentKind::Counter); + GaugeAggregator agg_long(metrics_api::InstrumentKind::Counter); + GaugeAggregator agg_float(metrics_api::InstrumentKind::Counter); + GaugeAggregator agg_double(metrics_api::InstrumentKind::Counter); + + for (int i = 1; i <= 10; ++i) + { + agg_int.update(i); + agg_long.update(i); + } + + for (float i = 1.0; i <= 10.0; i += 1) + { + agg_float.update(i); + agg_double.update(i); + } + + ASSERT_EQ(agg_int.get_values()[0], 10); + ASSERT_EQ(agg_long.get_values()[0], 10); + ASSERT_EQ(agg_float.get_values()[0], 10.0); + ASSERT_EQ(agg_double.get_values()[0], 10.0); +} + +static void callback(GaugeAggregator &agg) +{ + for (int i = 1; i <= 10000; ++i) + { + agg.update(i); + } +} + +TEST(GaugeAggregator, Concurrency) +{ + // This test checks that the aggregator updates appropriately + // when called in a multi-threaded context. + GaugeAggregator agg(metrics_api::InstrumentKind::Counter); + + std::thread first(&callback, std::ref(agg)); + std::thread second(&callback, std::ref(agg)); + + first.join(); + second.join(); + + ASSERT_EQ(agg.get_values()[0], 10000); +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/histogram_aggregator_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/histogram_aggregator_test.cc new file mode 100644 index 000000000..daf920aa7 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/histogram_aggregator_test.cc @@ -0,0 +1,173 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/_metrics/aggregator/histogram_aggregator.h" + +# include +# include +# include +# include + +// #include + +namespace metrics_api = opentelemetry::metrics; + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +// Test updating with a uniform set of updates +TEST(Histogram, Uniform) +{ + std::vector boundaries{10, 20, 30, 40, 50}; + HistogramAggregator alpha(metrics_api::InstrumentKind::Counter, boundaries); + + EXPECT_EQ(alpha.get_aggregator_kind(), AggregatorKind::Histogram); + + alpha.checkpoint(); + EXPECT_EQ(alpha.get_checkpoint().size(), 2); + EXPECT_EQ(alpha.get_counts().size(), 6); + + for (int i = 0; i < 60; i++) + { + alpha.update(i); + } + + alpha.checkpoint(); + + EXPECT_EQ(alpha.get_checkpoint()[0], 1770); + EXPECT_EQ(alpha.get_checkpoint()[1], 60); + + std::vector correct = {10, 10, 10, 10, 10, 10}; + EXPECT_EQ(alpha.get_counts(), correct); +} + +// Test updating with a normal distribution +TEST(Histogram, Normal) +{ + std::vector boundaries{2, 4, 6, 8, 10, 12}; + HistogramAggregator alpha(metrics_api::InstrumentKind::Counter, boundaries); + + std::vector vals{1, 3, 3, 5, 5, 5, 7, 7, 7, 7, 9, 9, 9, 11, 11, 13}; + for (int i : vals) + { + alpha.update(i); + } + + alpha.checkpoint(); + + EXPECT_EQ(alpha.get_checkpoint()[0], std::accumulate(vals.begin(), vals.end(), 0)); + EXPECT_EQ(alpha.get_checkpoint()[1], vals.size()); + + std::vector correct = {1, 2, 3, 4, 3, 2, 1}; + EXPECT_EQ(alpha.get_counts(), correct); +} + +TEST(Histogram, Merge) +{ + std::vector boundaries{2, 4, 6, 8, 10, 12}; + HistogramAggregator alpha(metrics_api::InstrumentKind::Counter, boundaries); + HistogramAggregator beta(metrics_api::InstrumentKind::Counter, boundaries); + + std::vector vals{1, 3, 3, 5, 5, 5, 7, 7, 7, 7, 9, 9, 9, 11, 11, 13}; + for (int i : vals) + { + alpha.update(i); + } + + std::vector otherVals{1, 1, 1, 1, 11, 11, 13, 13, 13, 15}; + for (int i : otherVals) + { + beta.update(i); + } + + alpha.merge(beta); + alpha.checkpoint(); + + EXPECT_EQ(alpha.get_checkpoint()[0], std::accumulate(vals.begin(), vals.end(), 0) + + std::accumulate(otherVals.begin(), otherVals.end(), 0)); + EXPECT_EQ(alpha.get_checkpoint()[1], vals.size() + otherVals.size()); + + std::vector correct = {5, 2, 3, 4, 3, 4, 5}; + EXPECT_EQ(alpha.get_counts(), correct); +} + +// Update callback used to validate multi-threaded performance +void histogramUpdateCallback(Aggregator &agg, std::vector vals) +{ + for (int i : vals) + { + agg.update(i); + } +} + +int randVal() +{ + return rand() % 15; +} + +TEST(Histogram, Concurrency) +{ + std::vector boundaries{2, 4, 6, 8, 10, 12}; + HistogramAggregator alpha(metrics_api::InstrumentKind::Counter, boundaries); + + std::vector vals1(1000); + std::generate(vals1.begin(), vals1.end(), randVal); + + std::vector vals2(1000); + std::generate(vals2.begin(), vals2.end(), randVal); + + std::thread first(histogramUpdateCallback, std::ref(alpha), vals1); + std::thread second(histogramUpdateCallback, std::ref(alpha), vals2); + + first.join(); + second.join(); + + HistogramAggregator beta(metrics_api::InstrumentKind::Counter, boundaries); + + // Timing harness to compare linear and binary insertion + // auto start = std::chrono::system_clock::now(); + for (int i : vals1) + { + beta.update(i); + } + for (int i : vals2) + { + beta.update(i); + } + // auto end = std::chrono::system_clock::now(); + // auto elapsed = std::chrono::duration_cast(end - start); + // std::cout <<"Update time: " < boundaries{2, 4, 6, 8, 10, 12}; + std::vector boundaries2{1, 4, 6, 8, 10, 12}; + std::vector unsortedBoundaries{10, 12, 4, 6, 8}; + EXPECT_ANY_THROW( + HistogramAggregator alpha(metrics_api::InstrumentKind::Counter, unsortedBoundaries)); + + HistogramAggregator beta(metrics_api::InstrumentKind::Counter, boundaries); + HistogramAggregator gamma(metrics_api::InstrumentKind::Counter, boundaries2); + + EXPECT_ANY_THROW(beta.merge(gamma)); +} + +# endif + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/meter_provider_sdk_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/meter_provider_sdk_test.cc new file mode 100644 index 000000000..2181bb62a --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/meter_provider_sdk_test.cc @@ -0,0 +1,26 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_METRICS_PREVIEW +# include + +# include "opentelemetry/sdk/_metrics/meter.h" +# include "opentelemetry/sdk/_metrics/meter_provider.h" + +using namespace opentelemetry::sdk::metrics; + +TEST(MeterProvider, GetMeter) +{ + MeterProvider tf; + auto t1 = tf.GetMeter("test"); + auto t2 = tf.GetMeter("test"); + auto t3 = tf.GetMeter("different", "1.0.0"); + ASSERT_NE(nullptr, t1); + ASSERT_NE(nullptr, t2); + ASSERT_NE(nullptr, t3); + + // Should return the same instance each time. + ASSERT_EQ(t1, t2); + ASSERT_EQ(t1, t3); +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/meter_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/meter_test.cc new file mode 100644 index 000000000..73ff1f9bd --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/meter_test.cc @@ -0,0 +1,297 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_METRICS_PREVIEW +# include +# include + +# include "opentelemetry/sdk/_metrics/meter.h" + +using namespace opentelemetry::sdk::metrics; +namespace metrics_api = opentelemetry::metrics; +namespace common = opentelemetry::common; +namespace nostd = opentelemetry::nostd; + +OPENTELEMETRY_BEGIN_NAMESPACE + +TEST(Meter, CreateSyncInstruments) +{ + // Test that there are no errors creating synchronous instruments. + Meter m("Test"); + + m.NewShortCounter("Test-short-counter", "For testing", "Unitless", true); + m.NewIntCounter("Test-int-counter", "For testing", "Unitless", true); + m.NewFloatCounter("Test-float-counter", "For testing", "Unitless", true); + m.NewDoubleCounter("Test-double-counter", "For testing", "Unitless", true); + + m.NewShortUpDownCounter("Test-short-ud-counter", "For testing", "Unitless", true); + m.NewIntUpDownCounter("Test-int-ud-counter", "For testing", "Unitless", true); + m.NewFloatUpDownCounter("Test-float-ud-counter", "For testing", "Unitless", true); + m.NewDoubleUpDownCounter("Test-double-ud-counter", "For testing", "Unitless", true); + + m.NewShortValueRecorder("Test-short-recorder", "For testing", "Unitless", true); + m.NewIntValueRecorder("Test-int-recorder", "For testing", "Unitless", true); + m.NewFloatValueRecorder("Test-float-recorder", "For testing", "Unitless", true); + m.NewDoubleValueRecorder("Test-double-recorder", "For testing", "Unitless", true); +} + +// Dummy functions for asynchronous instrument constructors +void ShortCallback(metrics_api::ObserverResult) {} +void IntCallback(metrics_api::ObserverResult) {} +void FloatCallback(metrics_api::ObserverResult) {} +void DoubleCallback(metrics_api::ObserverResult) {} + +TEST(Meter, CreateAsyncInstruments) +{ + // Test that there are no errors when creating asynchronous instruments. + Meter m("Test"); + + m.NewShortSumObserver("Test-short-sum-obs", "For testing", "Unitless", true, &ShortCallback); + m.NewIntSumObserver("Test-int-sum-obs", "For testing", "Unitless", true, &IntCallback); + m.NewFloatSumObserver("Test-float-sum-obs", "For testing", "Unitless", true, &FloatCallback); + m.NewDoubleSumObserver("Test-double-sum-obs", "For testing", "Unitless", true, &DoubleCallback); + + m.NewShortUpDownSumObserver("Test-short-ud-sum-obs", "For testing", "Unitless", true, + &ShortCallback); + m.NewIntUpDownSumObserver("Test-int-ud-sum-obs", "For testing", "Unitless", true, &IntCallback); + m.NewFloatUpDownSumObserver("Test-float-ud-sum-obs", "For testing", "Unitless", true, + &FloatCallback); + m.NewDoubleUpDownSumObserver("Test-double-ud-sum-obs", "For testing", "Unitless", true, + &DoubleCallback); + + m.NewShortValueObserver("Test-short-val-obs", "For testing", "Unitless", true, &ShortCallback); + m.NewIntValueObserver("Test-int-val-obs", "For testing", "Unitless", true, &IntCallback); + m.NewFloatValueObserver("Test-float-val-obs", "For testing", "Unitless", true, &FloatCallback); + m.NewDoubleValueObserver("Test-double-val-obs", "For testing", "Unitless", true, &DoubleCallback); +} + +TEST(Meter, CollectSyncInstruments) +{ + // Verify that the records returned on a call to Collect() are correct for synchronous + // instruments. + Meter m("Test"); + + ASSERT_EQ(m.Collect().size(), 0); + + auto counter = m.NewShortCounter("Test-counter", "For testing", "Unitless", true); + + std::map labels = {{"Key", "Value"}}; + auto labelkv = common::KeyValueIterableView{labels}; + + counter->add(1, labelkv); + + std::vector res = m.Collect(); + auto agg_var = res[0].GetAggregator(); + auto agg = nostd::get<0>(agg_var); + + ASSERT_EQ(agg->get_checkpoint()[0], 1); + + // Now call add() and Collect() again to ensure that the value in the underlying + // aggregator was reset to the default. + + counter->add(10, labelkv); + + res = m.Collect(); + agg_var = res[0].GetAggregator(); + agg = nostd::get<0>(agg_var); + + ASSERT_EQ(agg->get_checkpoint()[0], 10); +} + +TEST(Meter, CollectDeletedSync) +{ + // Verify that calling Collect() after creating a synchronous instrument and destroying + // the return pointer does not result in a segfault. + + Meter m("Test"); + + ASSERT_EQ(m.Collect().size(), 0); + + std::map labels = {{"Key", "Value"}}; + auto labelkv = common::KeyValueIterableView{labels}; + { + auto counter = m.NewShortCounter("Test-counter", "For testing", "Unitless", true); + counter->add(1, labelkv); + } // counter shared_ptr deleted here + + std::vector res = m.Collect(); + auto agg_var = res[0].GetAggregator(); + auto agg = nostd::get<0>(agg_var); + + ASSERT_EQ(agg->get_checkpoint()[0], 1); +} + +// Dummy function for asynchronous instrument constructors. +void Callback(metrics_api::ObserverResult result) +{ + std::map labels = {{"key", "value"}}; + auto labelkv = common::KeyValueIterableView{labels}; + result.observe(1, labelkv); +} + +TEST(Meter, CollectAsyncInstruments) +{ + // Verify that the records returned on a call to Collect() are correct for asynchronous + // instruments. + Meter m("Test"); + + ASSERT_EQ(m.Collect().size(), 0); + + auto sumobs = + m.NewShortSumObserver("Test-counter", "For testing", "Unitless", true, &ShortCallback); + + std::map labels = {{"Key", "Value"}}; + auto labelkv = common::KeyValueIterableView{labels}; + + sumobs->observe(1, labelkv); + + std::vector res = m.Collect(); + auto agg_var = res[0].GetAggregator(); + auto agg = nostd::get<0>(agg_var); + + ASSERT_EQ(agg->get_checkpoint()[0], 1); + + // Now call observe() and Collect() again to ensure that the value in the underlying + // aggregator was reset to the default. + + sumobs->observe(10, labelkv); + + res = m.Collect(); + agg_var = res[0].GetAggregator(); + agg = nostd::get<0>(agg_var); + + ASSERT_EQ(agg->get_checkpoint()[0], 10); +} + +TEST(Meter, CollectDeletedAsync) +{ + // Verify that calling Collect() after creating an asynchronous instrument and destroying + // the return pointer does not result in a segfault. + + Meter m("Test"); + + ASSERT_EQ(m.Collect().size(), 0); + + std::map labels = {{"Key", "Value"}}; + auto labelkv = common::KeyValueIterableView{labels}; + { + auto sumobs = m.NewShortSumObserver("Test-counter", "For testing", "Unitless", true, &Callback); + sumobs->observe(1, labelkv); + } // sumobs shared_ptr deleted here + + std::vector res = m.Collect(); + auto agg_var = res[0].GetAggregator(); + auto agg = nostd::get<0>(agg_var); + + ASSERT_EQ(agg->get_checkpoint()[0], 1); +} + +TEST(Meter, RecordBatch) +{ + // This tests that RecordBatch appropriately updates the aggregators of the instruments + // passed to the function. Short, int, float, and double data types are tested. + Meter m("Test"); + + auto scounter = m.NewShortCounter("Test-scounter", "For testing", "Unitless", true); + auto icounter = m.NewIntCounter("Test-icounter", "For testing", "Unitless", true); + auto fcounter = m.NewFloatCounter("Test-fcounter", "For testing", "Unitless", true); + auto dcounter = m.NewDoubleCounter("Test-dcounter", "For testing", "Unitless", true); + + std::map labels = {{"Key", "Value"}}; + auto labelkv = common::KeyValueIterableView{labels}; + + metrics_api::SynchronousInstrument *sinstr_arr[] = {scounter.get()}; + short svalues_arr[] = {1}; + + nostd::span *> sinstrs{sinstr_arr}; + nostd::span svalues{svalues_arr}; + + m.RecordShortBatch(labelkv, sinstrs, svalues); + std::vector res = m.Collect(); + auto short_agg_var = res[0].GetAggregator(); + auto short_agg = nostd::get<0>(short_agg_var); + ASSERT_EQ(short_agg->get_checkpoint()[0], 1); + + metrics_api::SynchronousInstrument *iinstr_arr[] = {icounter.get()}; + int ivalues_arr[] = {1}; + + nostd::span *> iinstrs{iinstr_arr}; + nostd::span ivalues{ivalues_arr}; + + m.RecordIntBatch(labelkv, iinstrs, ivalues); + res = m.Collect(); + auto int_agg_var = res[0].GetAggregator(); + auto int_agg = nostd::get<1>(int_agg_var); + ASSERT_EQ(int_agg->get_checkpoint()[0], 1); + + metrics_api::SynchronousInstrument *finstr_arr[] = {fcounter.get()}; + float fvalues_arr[] = {1.0}; + + nostd::span *> finstrs{finstr_arr}; + nostd::span fvalues{fvalues_arr}; + + m.RecordFloatBatch(labelkv, finstrs, fvalues); + res = m.Collect(); + auto float_agg_var = res[0].GetAggregator(); + auto float_agg = nostd::get<2>(float_agg_var); + ASSERT_EQ(float_agg->get_checkpoint()[0], 1.0); + + metrics_api::SynchronousInstrument *dinstr_arr[] = {dcounter.get()}; + double dvalues_arr[] = {1.0}; + + nostd::span *> dinstrs{dinstr_arr}; + nostd::span dvalues{dvalues_arr}; + + m.RecordDoubleBatch(labelkv, dinstrs, dvalues); + res = m.Collect(); + auto double_agg_var = res[0].GetAggregator(); + auto double_agg = nostd::get<3>(double_agg_var); + ASSERT_EQ(double_agg->get_checkpoint()[0], 1.0); +} + +TEST(Meter, DisableCollectSync) +{ + Meter m("Test"); + std::map labels = {{"Key", "Value"}}; + auto labelkv = common::KeyValueIterableView{labels}; + auto c = m.NewShortCounter("c", "", "", false); + c->add(1, labelkv); + ASSERT_EQ(m.Collect().size(), 0); +} + +TEST(Meter, DisableCollectAsync) +{ + Meter m("Test"); + std::map labels = {{"Key", "Value"}}; + auto labelkv = common::KeyValueIterableView{labels}; + auto c = m.NewShortValueObserver("c", "", "", false, &ShortCallback); + c->observe(1, labelkv); + ASSERT_EQ(m.Collect().size(), 0); +} + +TEST(MeterStringUtil, IsValid) +{ +# if __EXCEPTIONS + Meter m("Test"); + ASSERT_ANY_THROW(m.NewShortCounter("", "Empty name is invalid", " ", true)); + ASSERT_ANY_THROW(m.NewShortCounter("1a", "Can't begin with a number", " ", true)); + ASSERT_ANY_THROW(m.NewShortCounter(".a", "Can't begin with punctuation", " ", true)); + ASSERT_ANY_THROW(m.NewShortCounter(" a", "Can't begin with space", " ", true)); + ASSERT_ANY_THROW(m.NewShortCounter( + "te^ s=%t", "Only alphanumeric ., -, and _ characters are allowed", " ", true)); +# endif +} + +TEST(MeterStringUtil, AlreadyExists) +{ +# if __EXCEPTIONS + Meter m("Test"); + + m.NewShortCounter("a", "First instance of instrument named 'a'", "", true); + ASSERT_ANY_THROW(m.NewShortCounter("a", "Second (illegal) instrument named 'a'", "", true)); + ASSERT_ANY_THROW(m.NewShortSumObserver("a", "Still illegal even though it is not a short counter", + "", true, &ShortCallback)); +# endif +} +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/metric_instrument_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/metric_instrument_test.cc new file mode 100644 index 000000000..28df53fc3 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/metric_instrument_test.cc @@ -0,0 +1,487 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_METRICS_PREVIEW + +# include +# include +# include +# include +# include +# include +# include + +# include "opentelemetry/common/macros.h" +# include "opentelemetry/sdk/_metrics/async_instruments.h" +# include "opentelemetry/sdk/_metrics/sync_instruments.h" + +namespace metrics_api = opentelemetry::metrics; + +# ifdef OPENTELEMETRY_RTTI_ENABLED +# define METRICS_TEST_TYPE_CAST(TO, FROM) dynamic_cast(FROM) +# else +# define METRICS_TEST_TYPE_CAST(TO, FROM) static_cast(FROM) +# endif + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +void ObserverConstructorCallback(metrics_api::ObserverResult result) +{ + std::map labels = {{"key", "value"}}; + auto labelkv = common::KeyValueIterableView{labels}; + result.observe(1, labelkv); +} + +TEST(ApiSdkConversion, async) +{ + nostd::shared_ptr> alpha = + nostd::shared_ptr>( + new ValueObserver("ankit", "none", "unitles", true, &ObserverConstructorCallback)); + + std::map labels = {{"key587", "value264"}}; + auto labelkv = common::KeyValueIterableView{labels}; + + alpha->observe(123456, labelkv); + EXPECT_EQ(METRICS_TEST_TYPE_CAST(AsynchronousInstrument *, alpha.get()) + ->GetRecords()[0] + .GetLabels(), + "{key587:value264}"); + + alpha->observe(123456, labelkv); + AggregatorVariant canCollect = METRICS_TEST_TYPE_CAST(AsynchronousInstrument *, alpha.get()) + ->GetRecords()[0] + .GetAggregator(); + EXPECT_EQ(nostd::holds_alternative>>(canCollect), false); + EXPECT_EQ(nostd::holds_alternative>>(canCollect), true); + EXPECT_EQ(nostd::get>>(canCollect)->get_checkpoint()[0], 123456); +} + +TEST(IntValueObserver, InstrumentFunctions) +{ + ValueObserver alpha("enabled", "no description", "unitless", true, + &ObserverConstructorCallback); + std::map labels = {{"key", "value"}}; + auto labelkv = common::KeyValueIterableView{labels}; + + EXPECT_EQ(alpha.GetName(), "enabled"); + EXPECT_EQ(alpha.GetDescription(), "no description"); + EXPECT_EQ(alpha.GetUnits(), "unitless"); + EXPECT_EQ(alpha.IsEnabled(), true); + EXPECT_EQ(alpha.GetKind(), metrics_api::InstrumentKind::ValueObserver); + + alpha.run(); + EXPECT_EQ(alpha.boundAggregators_[KvToString(labelkv)]->get_values()[0], 1); // min +} + +void ObserverCallback(std::shared_ptr> in, + int freq, + const common::KeyValueIterable &labels) +{ + for (int i = 0; i < freq; i++) + { + in->observe(i, labels); + } +} + +void NegObserverCallback(std::shared_ptr> in, + int freq, + const common::KeyValueIterable &labels) +{ + for (int i = 0; i < freq; i++) + { + in->observe(-i, labels); + } +} + +TEST(IntValueObserver, StressObserve) +{ + std::shared_ptr> alpha(new ValueObserver( + "enabled", "no description", "unitless", true, &ObserverConstructorCallback)); + + std::map labels = {{"key", "value"}}; + std::map labels1 = {{"key1", "value1"}}; + auto labelkv = common::KeyValueIterableView{labels}; + auto labelkv1 = common::KeyValueIterableView{labels1}; + + std::thread first(ObserverCallback, alpha, 25, + labelkv); // spawn new threads that call the callback + std::thread second(ObserverCallback, alpha, 25, labelkv); + std::thread third(ObserverCallback, alpha, 25, labelkv1); + std::thread fourth(NegObserverCallback, alpha, 25, labelkv1); // negative values + + first.join(); + second.join(); + third.join(); + fourth.join(); + + EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv)]->get_values()[0], 0); // min + EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv)]->get_values()[1], 24); // max + EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv)]->get_values()[2], 600); // sum + EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv)]->get_values()[3], 50); // count + + EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv1)]->get_values()[0], -24); // min + EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv1)]->get_values()[1], 24); // max + EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv1)]->get_values()[2], 0); // sum + EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv1)]->get_values()[3], 50); // count +} + +void SumObserverCallback(std::shared_ptr> in, + int freq, + const common::KeyValueIterable &labels) +{ + for (int i = 0; i < freq; i++) + { + in->observe(1, labels); + } +} + +TEST(IntSumObserver, StressObserve) +{ + std::shared_ptr> alpha( + new SumObserver("test", "none", "unitless", true, &ObserverConstructorCallback)); + + std::map labels = {{"key", "value"}}; + std::map labels1 = {{"key1", "value1"}}; + auto labelkv = common::KeyValueIterableView{labels}; + auto labelkv1 = common::KeyValueIterableView{labels1}; + + std::thread first(SumObserverCallback, alpha, 100000, labelkv); + std::thread second(SumObserverCallback, alpha, 100000, labelkv); + std::thread third(SumObserverCallback, alpha, 300000, labelkv1); + + first.join(); + second.join(); + third.join(); + + EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv)]->get_values()[0], 200000); + EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv1)]->get_values()[0], 300000); +} + +void UpDownSumObserverCallback(std::shared_ptr> in, + int freq, + const common::KeyValueIterable &labels) +{ + for (int i = 0; i < freq; i++) + { + in->observe(1, labels); + } +} + +void NegUpDownSumObserverCallback(std::shared_ptr> in, + int freq, + const common::KeyValueIterable &labels) +{ + for (int i = 0; i < freq; i++) + { + in->observe(-1, labels); + } +} + +TEST(IntUpDownObserver, StressAdd) +{ + std::shared_ptr> alpha( + new UpDownSumObserver("test", "none", "unitless", true, &ObserverConstructorCallback)); + + std::map labels = {{"key", "value"}}; + std::map labels1 = {{"key1", "value1"}}; + auto labelkv = common::KeyValueIterableView{labels}; + auto labelkv1 = common::KeyValueIterableView{labels1}; + + std::thread first(UpDownSumObserverCallback, alpha, 12340, + labelkv); // spawn new threads that call the callback + std::thread second(UpDownSumObserverCallback, alpha, 12340, labelkv); + std::thread third(UpDownSumObserverCallback, alpha, 56780, labelkv1); + std::thread fourth(NegUpDownSumObserverCallback, alpha, 12340, labelkv1); // negative values + + first.join(); + second.join(); + third.join(); + fourth.join(); + + EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv)]->get_values()[0], 12340 * 2); + EXPECT_EQ(alpha->boundAggregators_[KvToString(labelkv1)]->get_values()[0], 56780 - 12340); +} + +TEST(Counter, InstrumentFunctions) +{ + Counter alpha("enabled", "no description", "unitless", true); + Counter beta("not enabled", "some description", "units", false); + + EXPECT_EQ(static_cast(alpha.GetName()), "enabled"); + EXPECT_EQ(static_cast(alpha.GetDescription()), "no description"); + EXPECT_EQ(static_cast(alpha.GetUnits()), "unitless"); + EXPECT_EQ(alpha.IsEnabled(), true); + + EXPECT_EQ(static_cast(beta.GetName()), "not enabled"); + EXPECT_EQ(static_cast(beta.GetDescription()), "some description"); + EXPECT_EQ(static_cast(beta.GetUnits()), "units"); + EXPECT_EQ(beta.IsEnabled(), false); +} + +TEST(Counter, Binding) +{ + Counter alpha("test", "none", "unitless", true); + + std::map labels = {{"key", "value"}}; + std::map labels1 = {{"key1", "value1"}}; + std::map labels2 = {{"key2", "value2"}, {"key3", "value3"}}; + std::map labels3 = {{"key3", "value3"}, {"key2", "value2"}}; + + auto labelkv = common::KeyValueIterableView{labels}; + auto labelkv1 = common::KeyValueIterableView{labels1}; + auto labelkv2 = common::KeyValueIterableView{labels2}; + auto labelkv3 = common::KeyValueIterableView{labels3}; + + auto beta = alpha.bindCounter(labelkv); + auto gamma = alpha.bindCounter(labelkv1); + auto delta = alpha.bindCounter(labelkv1); + auto epsilon = alpha.bindCounter(labelkv1); + auto zeta = alpha.bindCounter(labelkv2); + auto eta = alpha.bindCounter(labelkv3); + + EXPECT_EQ(beta->get_ref(), 1); + EXPECT_EQ(gamma->get_ref(), 3); + EXPECT_EQ(eta->get_ref(), 2); + + delta->unbind(); + gamma->unbind(); + epsilon->unbind(); + + EXPECT_EQ(alpha.boundInstruments_[KvToString(labelkv1)]->get_ref(), 0); + EXPECT_EQ(alpha.boundInstruments_.size(), 3); +} + +TEST(Counter, getAggsandnewupdate) +{ + Counter alpha("test", "none", "unitless", true); + + std::map labels = {{"key3", "value3"}, {"key2", "value2"}}; + + auto labelkv = common::KeyValueIterableView{labels}; + auto beta = alpha.bindCounter(labelkv); + beta->add(1); + beta->unbind(); + + EXPECT_EQ(alpha.boundInstruments_[KvToString(labelkv)]->get_ref(), 0); + EXPECT_EQ(alpha.boundInstruments_.size(), 1); + + auto theta = alpha.GetRecords(); + EXPECT_EQ(theta.size(), 1); + EXPECT_EQ(theta[0].GetName(), "test"); + EXPECT_EQ(theta[0].GetDescription(), "none"); + EXPECT_EQ(theta[0].GetLabels(), "{key2:value2,key3:value3}"); +} + +void CounterCallback(std::shared_ptr> in, + int freq, + const common::KeyValueIterable &labels) +{ + for (int i = 0; i < freq; i++) + { + in->add(1, labels); + } +} + +TEST(Counter, StressAdd) +{ + std::shared_ptr> alpha(new Counter("test", "none", "unitless", true)); + + std::map labels = {{"key", "value"}}; + std::map labels1 = {{"key1", "value1"}}; + + auto labelkv = common::KeyValueIterableView{labels}; + auto labelkv1 = common::KeyValueIterableView{labels1}; + + std::thread first(CounterCallback, alpha, 1000, labelkv); + std::thread second(CounterCallback, alpha, 1000, labelkv); + std::thread third(CounterCallback, alpha, 3000, labelkv1); + + first.join(); + second.join(); + third.join(); + + EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundCounter *, + alpha->boundInstruments_[KvToString(labelkv)].get()) + ->GetAggregator() + ->get_values()[0], + 2000); + EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundCounter *, + alpha->boundInstruments_[KvToString(labelkv1)].get()) + ->GetAggregator() + ->get_values()[0], + 3000); +} + +void UpDownCounterCallback(std::shared_ptr> in, + int freq, + const common::KeyValueIterable &labels) +{ + for (int i = 0; i < freq; i++) + { + in->add(1, labels); + } +} + +void NegUpDownCounterCallback(std::shared_ptr> in, + int freq, + const common::KeyValueIterable &labels) +{ + for (int i = 0; i < freq; i++) + { + in->add(-1, labels); + } +} + +TEST(IntUpDownCounter, StressAdd) +{ + std::shared_ptr> alpha( + new UpDownCounter("test", "none", "unitless", true)); + + std::map labels = {{"key", "value"}}; + std::map labels1 = {{"key1", "value1"}}; + auto labelkv = common::KeyValueIterableView{labels}; + auto labelkv1 = common::KeyValueIterableView{labels1}; + + std::thread first(UpDownCounterCallback, alpha, 12340, + labelkv); // spawn new threads that call the callback + std::thread second(UpDownCounterCallback, alpha, 12340, labelkv); + std::thread third(UpDownCounterCallback, alpha, 56780, labelkv1); + std::thread fourth(NegUpDownCounterCallback, alpha, 12340, labelkv1); // negative values + + first.join(); + second.join(); + third.join(); + fourth.join(); + + EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundUpDownCounter *, + alpha->boundInstruments_[KvToString(labelkv)].get()) + ->GetAggregator() + ->get_values()[0], + 12340 * 2); + EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundUpDownCounter *, + alpha->boundInstruments_[KvToString(labelkv1)].get()) + ->GetAggregator() + ->get_values()[0], + 56780 - 12340); +} + +void RecorderCallback(std::shared_ptr> in, + int freq, + const common::KeyValueIterable &labels) +{ + for (int i = 0; i < freq; i++) + { + in->record(i, labels); + } +} + +void NegRecorderCallback(std::shared_ptr> in, + int freq, + const common::KeyValueIterable &labels) +{ + for (int i = 0; i < freq; i++) + { + in->record(-i, labels); + } +} + +TEST(IntValueRecorder, StressRecord) +{ + std::shared_ptr> alpha( + new ValueRecorder("test", "none", "unitless", true)); + + std::map labels = {{"key", "value"}}; + std::map labels1 = {{"key1", "value1"}}; + auto labelkv = common::KeyValueIterableView{labels}; + auto labelkv1 = common::KeyValueIterableView{labels1}; + + std::thread first(RecorderCallback, alpha, 25, + labelkv); // spawn new threads that call the callback + std::thread second(RecorderCallback, alpha, 50, labelkv); + std::thread third(RecorderCallback, alpha, 25, labelkv1); + std::thread fourth(NegRecorderCallback, alpha, 100, labelkv1); // negative values + + first.join(); + second.join(); + third.join(); + fourth.join(); + + EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundValueRecorder *, + alpha->boundInstruments_[KvToString(labelkv)].get()) + ->GetAggregator() + ->get_values()[0], + 0); // min + EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundValueRecorder *, + alpha->boundInstruments_[KvToString(labelkv)].get()) + ->GetAggregator() + ->get_values()[1], + 49); // max + EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundValueRecorder *, + alpha->boundInstruments_[KvToString(labelkv)].get()) + ->GetAggregator() + ->get_values()[2], + 1525); // sum + EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundValueRecorder *, + alpha->boundInstruments_[KvToString(labelkv)].get()) + ->GetAggregator() + ->get_values()[3], + 75); // count + + EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundValueRecorder *, + alpha->boundInstruments_[KvToString(labelkv1)].get()) + ->GetAggregator() + ->get_values()[0], + -99); // min + EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundValueRecorder *, + alpha->boundInstruments_[KvToString(labelkv1)].get()) + ->GetAggregator() + ->get_values()[1], + 24); // max + EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundValueRecorder *, + alpha->boundInstruments_[KvToString(labelkv1)].get()) + ->GetAggregator() + ->get_values()[2], + -4650); // sum + EXPECT_EQ(METRICS_TEST_TYPE_CAST(BoundValueRecorder *, + alpha->boundInstruments_[KvToString(labelkv1)].get()) + ->GetAggregator() + ->get_values()[3], + 125); // count +} + +TEST(Instruments, NoUpdateNoRecord) +{ + // This test verifies that instruments that have received no updates + // in the last collection period are not made into records for export. + + Counter alpha("alpha", "no description", "unitless", true); + + std::map labels = {{"key", "value"}}; + + auto labelkv = common::KeyValueIterableView{labels}; + + EXPECT_EQ(alpha.GetRecords().size(), 0); + alpha.add(1, labelkv); + EXPECT_EQ(alpha.GetRecords().size(), 1); + + UpDownCounter beta("beta", "no description", "unitless", true); + + EXPECT_EQ(beta.GetRecords().size(), 0); + beta.add(1, labelkv); + EXPECT_EQ(beta.GetRecords().size(), 1); + + ValueRecorder gamma("gamma", "no description", "unitless", true); + + EXPECT_EQ(gamma.GetRecords().size(), 0); + gamma.record(1, labelkv); + EXPECT_EQ(gamma.GetRecords().size(), 1); +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/min_max_sum_count_aggregator_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/min_max_sum_count_aggregator_test.cc new file mode 100644 index 000000000..3b657d955 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/min_max_sum_count_aggregator_test.cc @@ -0,0 +1,209 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_METRICS_PREVIEW +# include +# include + +# include "opentelemetry/sdk/_metrics/aggregator/min_max_sum_count_aggregator.h" + +using namespace opentelemetry::sdk::metrics; +namespace metrics_api = opentelemetry::metrics; + +TEST(MinMaxSumCountAggregator, Update) +{ + // This tests that the aggregator updates the maintained value correctly + // after a call to the update() function. + MinMaxSumCountAggregator agg(metrics_api::InstrumentKind::Counter); + auto value_set = agg.get_values(); + ASSERT_EQ(value_set[0], 0); + ASSERT_EQ(value_set[1], 0); + ASSERT_EQ(value_set[2], 0); + ASSERT_EQ(value_set[3], 0); + + // 1 + 2 + 3 + ... + 10 = 55 + for (int i = 1; i <= 10; ++i) + { + agg.update(i); + } + + value_set = agg.get_values(); + ASSERT_EQ(value_set[0], 1); // min + ASSERT_EQ(value_set[1], 10); // max + ASSERT_EQ(value_set[2], 55); // sum + ASSERT_EQ(value_set[3], 10); // count +} + +TEST(MinMaxSumCountAggregator, FirstUpdate) +{ + // This tests that the aggregator appropriately maintains the min and + // max values after a single update call. + MinMaxSumCountAggregator agg(metrics_api::InstrumentKind::Counter); + agg.update(1); + auto value_set = agg.get_values(); + ASSERT_EQ(value_set[0], 1); // min + ASSERT_EQ(value_set[1], 1); // max + ASSERT_EQ(value_set[2], 1); // sum + ASSERT_EQ(value_set[3], 1); // count +} + +TEST(MinMaxSumCountAggregator, Checkpoint) +{ + // This test verifies that the default checkpoint is set correctly + // and that the checkpoint values update correctly after a call + // to the checkpoint() function. + MinMaxSumCountAggregator agg(metrics_api::InstrumentKind::Counter); + + // Verify that the default checkpoint is set correctly. + auto checkpoint_set = agg.get_checkpoint(); + ASSERT_EQ(checkpoint_set[0], 0); // min + ASSERT_EQ(checkpoint_set[1], 0); // max + ASSERT_EQ(checkpoint_set[2], 0); // sum + ASSERT_EQ(checkpoint_set[3], 0); // count + + // 1 + 2 + 3 + ... + 10 = 55 + for (int i = 1; i <= 10; ++i) + { + agg.update(i); + } + + agg.checkpoint(); + + // Verify that the checkpoint values were updated. + checkpoint_set = agg.get_checkpoint(); + ASSERT_EQ(checkpoint_set[0], 1); // min + ASSERT_EQ(checkpoint_set[1], 10); // max + ASSERT_EQ(checkpoint_set[2], 55); // sum + ASSERT_EQ(checkpoint_set[3], 10); // count + + // Verify that the current values were reset to the default state. + auto value_set = agg.get_values(); + ASSERT_EQ(value_set[0], 0); // min + ASSERT_EQ(value_set[1], 0); // max + ASSERT_EQ(value_set[2], 0); // sum + ASSERT_EQ(value_set[3], 0); // count +} + +TEST(MinMaxSumCountAggregator, Merge) +{ + // This tests that the values_ vector is updated correctly after + // two aggregators are merged together. + MinMaxSumCountAggregator agg1(metrics_api::InstrumentKind::Counter); + MinMaxSumCountAggregator agg2(metrics_api::InstrumentKind::Counter); + + // 1 + 2 + 3 + ... + 10 = 55 + for (int i = 1; i <= 10; ++i) + { + agg1.update(i); + } + + // 1 + 2 + 3 + ... + 20 = 210 + for (int i = 1; i <= 20; ++i) + { + agg2.update(i); + } + + agg1.merge(agg2); + + // Verify that the current values were changed by the merge. + auto value_set = agg1.get_values(); + ASSERT_EQ(value_set[0], 1); // min + ASSERT_EQ(value_set[1], 20); // max + ASSERT_EQ(value_set[2], 265); // sum + ASSERT_EQ(value_set[3], 30); // count +} + +TEST(MinMaxSumCountAggregator, BadMerge) +{ + // This verifies that we encounter and error when we try to merge + // two aggregators of different numeric types together. + MinMaxSumCountAggregator agg1(metrics_api::InstrumentKind::Counter); + MinMaxSumCountAggregator agg2(metrics_api::InstrumentKind::ValueRecorder); + + agg1.update(1); + agg2.update(2); + + agg1.merge(agg2); + + // Verify that the values did NOT merge + auto value_set = agg1.get_values(); + ASSERT_EQ(value_set[0], 1); // min + ASSERT_EQ(value_set[0], 1); // max + ASSERT_EQ(value_set[0], 1); // sum + ASSERT_EQ(value_set[0], 1); // count +} + +TEST(MinMaxSumCountAggregator, Types) +{ + // This test verifies that we do not encounter any errors when + // using various numeric types. + MinMaxSumCountAggregator agg_int(metrics_api::InstrumentKind::Counter); + MinMaxSumCountAggregator agg_long(metrics_api::InstrumentKind::Counter); + MinMaxSumCountAggregator agg_float(metrics_api::InstrumentKind::Counter); + MinMaxSumCountAggregator agg_double(metrics_api::InstrumentKind::Counter); + + for (int i = 1; i <= 10; ++i) + { + agg_int.update(i); + agg_long.update(i); + } + + for (float i = 1.0; i <= 10.0; i += 1) + { + agg_float.update(i); + agg_double.update(i); + } + + auto value_set = agg_int.get_values(); + ASSERT_EQ(value_set[0], 1); // min + ASSERT_EQ(value_set[1], 10); // max + ASSERT_EQ(value_set[2], 55); // sum + ASSERT_EQ(value_set[3], 10); // count + + auto value_set2 = agg_long.get_values(); + ASSERT_EQ(value_set[0], 1); // min + ASSERT_EQ(value_set[1], 10); // max + ASSERT_EQ(value_set[2], 55); // sum + ASSERT_EQ(value_set[3], 10); // count + + auto value_set3 = agg_float.get_values(); + ASSERT_EQ(value_set[0], 1.0); // min + ASSERT_EQ(value_set[1], 10.0); // max + ASSERT_EQ(value_set[2], 55.0); // sum + ASSERT_EQ(value_set[3], 10); // count + + auto value_set4 = agg_double.get_values(); + ASSERT_EQ(value_set[0], 1.0); // min + ASSERT_EQ(value_set[1], 10.0); // max + ASSERT_EQ(value_set[2], 55.0); // sum + ASSERT_EQ(value_set[3], 10); // count +} + +static void callback(MinMaxSumCountAggregator &agg) +{ + // 1 + 2 + ... + 10000 = 50005000 + for (int i = 1; i <= 10000; ++i) + { + agg.update(i); + } +} + +TEST(MinMaxSumCountAggregator, Concurrency) +{ + // This test checks that the aggregator updates appropriately + // when called in a multi-threaded context. + MinMaxSumCountAggregator agg(metrics_api::InstrumentKind::Counter); + + std::thread first(&callback, std::ref(agg)); + std::thread second(&callback, std::ref(agg)); + + first.join(); + second.join(); + + auto value_set = agg.get_values(); + ASSERT_EQ(value_set[0], 1); + ASSERT_EQ(value_set[1], 10000); + ASSERT_EQ(value_set[2], 2 * 50005000); + ASSERT_EQ(value_set[3], 2 * 10000); +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/sketch_aggregator_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/sketch_aggregator_test.cc new file mode 100644 index 000000000..6dc2fbef3 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/sketch_aggregator_test.cc @@ -0,0 +1,254 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/_metrics/aggregator/sketch_aggregator.h" + +# include +# include +# include +# include + +namespace metrics_api = opentelemetry::metrics; + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +// Test updating with a uniform set of updates +TEST(Sketch, UniformValues) +{ + SketchAggregator alpha(metrics_api::InstrumentKind::ValueRecorder, .000005); + + EXPECT_EQ(alpha.get_aggregator_kind(), AggregatorKind::Sketch); + + alpha.checkpoint(); + EXPECT_EQ(alpha.get_checkpoint().size(), 2); + EXPECT_EQ(alpha.get_boundaries().size(), 0); + EXPECT_EQ(alpha.get_counts().size(), 0); + + for (int i = 0; i < 60; i++) + { + alpha.update(i); + } + + alpha.checkpoint(); + + EXPECT_EQ(alpha.get_boundaries().size(), 60); + EXPECT_EQ(alpha.get_counts().size(), 60); + + EXPECT_EQ(alpha.get_checkpoint()[0], 1770); + EXPECT_EQ(alpha.get_checkpoint()[1], 60); +} + +// Test updating with a normal distribution +TEST(Sketch, NormalValues) +{ + SketchAggregator alpha(metrics_api::InstrumentKind::ValueRecorder, .0005); + + std::vector vals{1, 3, 3, 5, 5, 5, 7, 7, 7, 7, 9, 9, 9, 11, 11, 13}; + for (int i : vals) + { + alpha.update(i); + } + alpha.checkpoint(); + + EXPECT_EQ(alpha.get_checkpoint()[0], std::accumulate(vals.begin(), vals.end(), 0)); + EXPECT_EQ(alpha.get_checkpoint()[1], vals.size()); + + std::vector correct = {1, 2, 3, 4, 3, 2, 1}; + EXPECT_EQ(alpha.get_counts(), correct); + + std::vector captured_bounds = alpha.get_boundaries(); + for (size_t i = 0; i < captured_bounds.size(); i++) + { + captured_bounds[i] = round(captured_bounds[i]); + } + + // It is not guaranteed that bounds are correct once the bucket sizes pass 1000 + std::vector correct_bounds = {1, 3, 5, 7, 9, 11, 13}; + EXPECT_EQ(captured_bounds, correct_bounds); +} + +int randVal() +{ + return rand() % 100000; +} + +/** Note that in this case, "Large" refers to a number of distinct values which exceed the maximum + * number of allowed buckets. + */ +TEST(Sketch, QuantileSmall) +{ + SketchAggregator alpha(metrics_api::InstrumentKind::ValueRecorder, .00005); + + std::vector vals1(2048); + std::generate(vals1.begin(), vals1.end(), randVal); + + std::vector vals2(2048); + std::generate(vals1.begin(), vals1.end(), randVal); + + for (int i : vals1) + { + alpha.update(i); + } + alpha.checkpoint(); + std::sort(vals1.begin(), vals1.end()); + + EXPECT_TRUE(abs(alpha.get_quantiles(.25) - vals1[2048 * .25 - 1]) <= 10); + EXPECT_TRUE(abs(alpha.get_quantiles(.50) - vals1[2048 * .50 - 1]) <= 10); + EXPECT_TRUE(abs(alpha.get_quantiles(.75) - vals1[2048 * .75 - 1]) <= 10); +} + +TEST(Sketch, UpdateQuantileLarge) +{ + SketchAggregator alpha(metrics_api::InstrumentKind::ValueRecorder, .0005, 7); + std::vector vals{1, 3, 3, 5, 5, 5, 7, 7, 7, 7, 9, 9, 9, 11, 11, 13}; + for (int i : vals) + { + alpha.update(i); + } + + // This addition should trigger the "1" and "3" buckets to merge + alpha.update(15); + alpha.checkpoint(); + + std::vector correct = {3, 3, 4, 3, 2, 1, 1}; + EXPECT_EQ(alpha.get_counts(), correct); + + for (int i : vals) + { + alpha.update(i); + } + alpha.update(15); + alpha.update(17); + alpha.checkpoint(); + + correct = {6, 4, 3, 2, 1, 1, 1}; + EXPECT_EQ(alpha.get_counts(), correct); +} + +TEST(Sketch, MergeSmall) +{ + SketchAggregator alpha(metrics_api::InstrumentKind::ValueRecorder, .0005); + SketchAggregator beta(metrics_api::InstrumentKind::ValueRecorder, .0005); + + std::vector vals{1, 3, 3, 5, 5, 5, 7, 7, 7, 7, 9, 9, 9, 11, 11, 13}; + for (int i : vals) + { + alpha.update(i); + } + + std::vector otherVals{1, 1, 1, 1, 11, 11, 13, 13, 13, 15}; + for (int i : otherVals) + { + beta.update(i); + } + + alpha.merge(beta); + alpha.checkpoint(); + + EXPECT_EQ(alpha.get_checkpoint()[0], std::accumulate(vals.begin(), vals.end(), 0) + + std::accumulate(otherVals.begin(), otherVals.end(), 0)); + EXPECT_EQ(alpha.get_checkpoint()[1], vals.size() + otherVals.size()); + + std::vector correct = {5, 2, 3, 4, 3, 4, 4, 1}; + EXPECT_EQ(alpha.get_counts(), correct); +} + +TEST(Sketch, MergeLarge) +{ + SketchAggregator alpha(metrics_api::InstrumentKind::ValueRecorder, .0005, 7); + SketchAggregator beta(metrics_api::InstrumentKind::ValueRecorder, .0005, 7); + + std::vector vals{1, 3, 3, 5, 5, 5, 7, 7, 7, 7, 9, 9, 9, 11, 11, 13}; + for (int i : vals) + { + alpha.update(i); + } + + std::vector otherVals{1, 1, 1, 1, 11, 11, 13, 13, 13, 15}; + for (int i : otherVals) + { + beta.update(i); + } + + alpha.merge(beta); + alpha.checkpoint(); + + EXPECT_EQ(alpha.get_checkpoint()[0], std::accumulate(vals.begin(), vals.end(), 0) + + std::accumulate(otherVals.begin(), otherVals.end(), 0)); + EXPECT_EQ(alpha.get_checkpoint()[1], vals.size() + otherVals.size()); + + std::vector correct = {7, 3, 4, 3, 4, 4, 1}; + EXPECT_EQ(alpha.get_counts(), correct); +} + +// Update callback used to validate multi-threaded performance +void sketchUpdateCallback(Aggregator &agg, std::vector vals) +{ + for (int i : vals) + { + agg.update(i); + } +} + +TEST(Sketch, Concurrency) +{ + SketchAggregator alpha(metrics_api::InstrumentKind::ValueRecorder, .0005, 20); + + std::vector vals1(1000); + std::generate(vals1.begin(), vals1.end(), randVal); + + std::vector vals2(1000); + std::generate(vals2.begin(), vals2.end(), randVal); + + std::thread first(sketchUpdateCallback, std::ref(alpha), vals1); + std::thread second(sketchUpdateCallback, std::ref(alpha), vals2); + + first.join(); + second.join(); + + SketchAggregator beta(metrics_api::InstrumentKind::ValueRecorder, .0005, 20); + + for (int i : vals1) + { + beta.update(i); + } + for (int i : vals2) + { + beta.update(i); + } + + alpha.checkpoint(); + beta.checkpoint(); + + EXPECT_EQ(alpha.get_checkpoint(), beta.get_checkpoint()); + EXPECT_EQ(alpha.get_counts(), beta.get_counts()); + EXPECT_EQ(alpha.get_boundaries(), beta.get_boundaries()); +} + +# if __EXCEPTIONS + +TEST(Sketch, Errors) +{ + + SketchAggregator tol1(metrics_api::InstrumentKind::ValueRecorder, .000005); + SketchAggregator tol2(metrics_api::InstrumentKind::ValueRecorder, .005); + SketchAggregator sz1(metrics_api::InstrumentKind::ValueRecorder, .000005, 2938); + SketchAggregator sz2(metrics_api::InstrumentKind::ValueRecorder, .000005); + + EXPECT_ANY_THROW(tol1.merge(tol2)); + EXPECT_ANY_THROW(sz1.merge(sz2)); + EXPECT_ANY_THROW(tol1.get_quantiles(-.000001)); + EXPECT_ANY_THROW(tol1.get_quantiles(1.000001)); +} + +# endif + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/ungrouped_processor_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/ungrouped_processor_test.cc new file mode 100644 index 000000000..934bfe23e --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/_metrics/ungrouped_processor_test.cc @@ -0,0 +1,601 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/_metrics/ungrouped_processor.h" +# include +# include "opentelemetry/nostd/shared_ptr.h" +# include "opentelemetry/sdk/_metrics/aggregator/counter_aggregator.h" + +namespace metric_sdk = opentelemetry::sdk::metrics; +namespace metrics_api = opentelemetry::metrics; +namespace nostd = opentelemetry::nostd; + +/* Test that CheckpointSelf() will return the amount of unique records in it, then + call FinishedCollection and see the map reset */ +TEST(UngroupedMetricsProcessor, UngroupedProcessorFinishedCollectionStateless) +{ + auto processor = std::unique_ptr( + new metric_sdk::UngroupedMetricsProcessor(false)); + + auto aggregator = std::shared_ptr>( + new metric_sdk::CounterAggregator(metrics_api::InstrumentKind::Counter)); + + auto aggregator2 = std::shared_ptr>( + new metric_sdk::CounterAggregator(metrics_api::InstrumentKind::Counter)); + + aggregator->update(5.5); + aggregator->checkpoint(); + + aggregator2->update(500.4); + aggregator2->checkpoint(); + + metric_sdk::Record r("name", "description", "labels", aggregator); + // Must have different (name, description, label, instrument) to map to + metric_sdk::Record r2("name2", "description2", "labels2", aggregator2); + + processor->process(r); + processor->process(r2); + + std::vector checkpoint = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint.size(), 2); + + processor->FinishedCollection(); + + checkpoint = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint.size(), 0); +} + +/* Test that CheckpointSelf() will return the amount of unique records in it, then + call FinishedCollection and see the map stay the same */ +TEST(UngroupedMetricsProcessor, UngroupedProcessorFinishedCollectionStateful) +{ + auto processor = std::unique_ptr( + new metric_sdk::UngroupedMetricsProcessor(true)); + + auto aggregator = std::shared_ptr>( + new metric_sdk::CounterAggregator(metrics_api::InstrumentKind::Counter)); + + auto aggregator2 = std::shared_ptr>( + new metric_sdk::CounterAggregator(metrics_api::InstrumentKind::Counter)); + + aggregator->update(5.5); + aggregator->checkpoint(); + + aggregator2->update(500.4); + aggregator2->checkpoint(); + + metric_sdk::Record r("name", "description", "labels", aggregator); + // Must have different (name, description, label, instrument) to map to + metric_sdk::Record r2("name2", "description2", "labels2", aggregator2); + + processor->process(r); + processor->process(r2); + + std::vector checkpoint = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint.size(), 2); + + processor->FinishedCollection(); + + ASSERT_EQ(checkpoint.size(), 2); +} + +// Test to make sure we keep information from record(short) that goes through process() +TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatelessShort) +{ + auto processor = std::unique_ptr( + new metric_sdk::UngroupedMetricsProcessor(false)); + + auto aggregator = std::shared_ptr>( + new metric_sdk::CounterAggregator(metrics_api::InstrumentKind::Counter)); + + aggregator->update(4); + aggregator->checkpoint(); + + metric_sdk::Record r("name", "description", "labels", aggregator); + + processor->process(r); + std::vector checkpoint = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint[0].GetName(), "name"); + ASSERT_EQ(checkpoint[0].GetLabels(), "labels"); + ASSERT_EQ(checkpoint[0].GetDescription(), "description"); + ASSERT_EQ( + nostd::get>>(checkpoint[0].GetAggregator()), + aggregator); +} + +// Test to make sure we keep information from record(int) that goes through process() +TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatelessInt) +{ + auto processor = std::unique_ptr( + new metric_sdk::UngroupedMetricsProcessor(false)); + + auto aggregator = std::shared_ptr>( + new metric_sdk::CounterAggregator(metrics_api::InstrumentKind::Counter)); + + aggregator->update(5); + aggregator->checkpoint(); + + metric_sdk::Record r("name", "description", "labels", aggregator); + + processor->process(r); + std::vector checkpoint = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint[0].GetName(), "name"); + ASSERT_EQ(checkpoint[0].GetLabels(), "labels"); + ASSERT_EQ(checkpoint[0].GetDescription(), "description"); + ASSERT_EQ(nostd::get>>(checkpoint[0].GetAggregator()), + aggregator); +} + +// Test to make sure we keep information from record(float) that goes through process() +TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatelessFloat) +{ + auto processor = std::unique_ptr( + new metric_sdk::UngroupedMetricsProcessor(false)); + + auto aggregator = std::shared_ptr>( + new metric_sdk::CounterAggregator(metrics_api::InstrumentKind::Counter)); + + aggregator->update(8.5); + aggregator->checkpoint(); + + metric_sdk::Record r("name", "description", "labels", aggregator); + + processor->process(r); + std::vector checkpoint = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint[0].GetName(), "name"); + ASSERT_EQ(checkpoint[0].GetLabels(), "labels"); + ASSERT_EQ(checkpoint[0].GetDescription(), "description"); + ASSERT_EQ( + nostd::get>>(checkpoint[0].GetAggregator()), + aggregator); +} + +// Test to make sure we keep information from record(double) that goes through process() +TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatelessDouble) +{ + auto processor = std::unique_ptr( + new metric_sdk::UngroupedMetricsProcessor(false)); + + auto aggregator = std::shared_ptr>( + new metric_sdk::CounterAggregator(metrics_api::InstrumentKind::Counter)); + + aggregator->update(5.5); + aggregator->checkpoint(); + + metric_sdk::Record r("name", "description", "labels", aggregator); + + processor->process(r); + std::vector checkpoint = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint[0].GetName(), "name"); + ASSERT_EQ(checkpoint[0].GetLabels(), "labels"); + ASSERT_EQ(checkpoint[0].GetDescription(), "description"); + ASSERT_EQ( + nostd::get>>(checkpoint[0].GetAggregator()), + aggregator); +} + +/** + * The following tests are for the Stateful version of the processor. These tests will make sure + * that when we send the same aggreagtor twice through process(), that the values will be merged. + * We can easily recreate this expected value by making a test aggregator that is updated through + * both process functions but only checkpointed at the end. + */ +TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulShort) +{ + auto processor = std::unique_ptr( + new metric_sdk::UngroupedMetricsProcessor(true)); + + auto aggregator = std::shared_ptr>( + new metric_sdk::CounterAggregator(metrics_api::InstrumentKind::Counter)); + + auto aggregator_test = std::shared_ptr>( + new metric_sdk::CounterAggregator(metrics_api::InstrumentKind::Counter)); + + aggregator->update(5); + aggregator_test->update(5); + aggregator->checkpoint(); + + metric_sdk::Record r("name", "description", "labels", aggregator); + + processor->process(r); + + std::vector checkpoint = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint[0].GetName(), "name"); + ASSERT_EQ(checkpoint[0].GetLabels(), "labels"); + ASSERT_EQ(checkpoint[0].GetDescription(), "description"); + ASSERT_EQ( + nostd::get>>(checkpoint[0].GetAggregator()) + ->get_checkpoint(), + aggregator->get_checkpoint()); + + aggregator->update(4); + aggregator_test->update(4); + aggregator->checkpoint(); + aggregator_test->checkpoint(); + + processor->process(r); + + std::vector checkpoint2 = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint2.size(), 1); + ASSERT_EQ( + nostd::get>>(checkpoint[0].GetAggregator()) + ->get_checkpoint()[0], + aggregator_test->get_checkpoint()[0]); +} + +TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulInt) +{ + auto processor = std::unique_ptr( + new metric_sdk::UngroupedMetricsProcessor(true)); + + auto aggregator = std::shared_ptr>( + new metric_sdk::CounterAggregator(metrics_api::InstrumentKind::Counter)); + + auto aggregator_test = std::shared_ptr>( + new metric_sdk::CounterAggregator(metrics_api::InstrumentKind::Counter)); + + aggregator->update(5); + aggregator_test->update(5); + aggregator->checkpoint(); + + metric_sdk::Record r("name", "description", "labels", aggregator); + + processor->process(r); + + std::vector checkpoint = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint[0].GetName(), "name"); + ASSERT_EQ(checkpoint[0].GetLabels(), "labels"); + ASSERT_EQ(checkpoint[0].GetDescription(), "description"); + ASSERT_EQ(nostd::get>>(checkpoint[0].GetAggregator()) + ->get_checkpoint(), + aggregator->get_checkpoint()); + + aggregator->update(4); + aggregator_test->update(4); + aggregator->checkpoint(); + aggregator_test->checkpoint(); + + processor->process(r); + + std::vector checkpoint2 = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint2.size(), 1); + ASSERT_EQ(nostd::get>>(checkpoint[0].GetAggregator()) + ->get_checkpoint()[0], + aggregator_test->get_checkpoint()[0]); +} + +TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulFloat) +{ + auto processor = std::unique_ptr( + new metric_sdk::UngroupedMetricsProcessor(true)); + + auto aggregator = std::shared_ptr>( + new metric_sdk::CounterAggregator(metrics_api::InstrumentKind::Counter)); + + auto aggregator_test = std::shared_ptr>( + new metric_sdk::CounterAggregator(metrics_api::InstrumentKind::Counter)); + + aggregator->update(5); + aggregator_test->update(5); + aggregator->checkpoint(); + + metric_sdk::Record r("name", "description", "labels", aggregator); + + processor->process(r); + + std::vector checkpoint = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint[0].GetName(), "name"); + ASSERT_EQ(checkpoint[0].GetLabels(), "labels"); + ASSERT_EQ(checkpoint[0].GetDescription(), "description"); + ASSERT_EQ( + nostd::get>>(checkpoint[0].GetAggregator()) + ->get_checkpoint(), + aggregator->get_checkpoint()); + + aggregator->update(4); + aggregator_test->update(4); + aggregator->checkpoint(); + aggregator_test->checkpoint(); + + processor->process(r); + + std::vector checkpoint2 = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint2.size(), 1); + ASSERT_EQ( + nostd::get>>(checkpoint[0].GetAggregator()) + ->get_checkpoint()[0], + aggregator_test->get_checkpoint()[0]); +} + +TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulDouble) +{ + auto processor = std::unique_ptr( + new metric_sdk::UngroupedMetricsProcessor(true)); + + auto aggregator = std::shared_ptr>( + new metric_sdk::CounterAggregator(metrics_api::InstrumentKind::Counter)); + + auto aggregator_test = std::shared_ptr>( + new metric_sdk::CounterAggregator(metrics_api::InstrumentKind::Counter)); + + aggregator->update(5.5); + aggregator_test->update(5.5); + aggregator->checkpoint(); + + metric_sdk::Record r("name", "description", "labels", aggregator); + + processor->process(r); + + std::vector checkpoint = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint[0].GetName(), "name"); + ASSERT_EQ(checkpoint[0].GetLabels(), "labels"); + ASSERT_EQ(checkpoint[0].GetDescription(), "description"); + ASSERT_EQ( + nostd::get>>(checkpoint[0].GetAggregator()) + ->get_checkpoint(), + aggregator->get_checkpoint()); + + aggregator->update(4.4); + aggregator_test->update(4.4); + aggregator->checkpoint(); + aggregator_test->checkpoint(); + + processor->process(r); + + std::vector checkpoint2 = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint2.size(), 1); + ASSERT_EQ( + nostd::get>>(checkpoint[0].GetAggregator()) + ->get_checkpoint()[0], + aggregator_test->get_checkpoint()[0]); +} + +TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulMinMaxSumCount) +{ + auto processor = std::unique_ptr( + new metric_sdk::UngroupedMetricsProcessor(true)); + + auto aggregator = std::shared_ptr>( + new metric_sdk::MinMaxSumCountAggregator(metrics_api::InstrumentKind::Counter)); + + auto aggregator2 = std::shared_ptr>( + new metric_sdk::MinMaxSumCountAggregator(metrics_api::InstrumentKind::Counter)); + + aggregator->update(1.1); + aggregator->update(2.2); + aggregator2->update(1.1); + aggregator2->update(2.2); + aggregator->checkpoint(); + + metric_sdk::Record r("name", "description", "labels", aggregator); + + processor->process(r); + + std::vector checkpoint = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint[0].GetName(), "name"); + ASSERT_EQ(checkpoint[0].GetLabels(), "labels"); + ASSERT_EQ(checkpoint[0].GetDescription(), "description"); + ASSERT_EQ( + nostd::get>>(checkpoint[0].GetAggregator()) + ->get_checkpoint(), + aggregator->get_checkpoint()); + + aggregator->update(5.5); + aggregator2->update(5.5); + aggregator->checkpoint(); + aggregator2->checkpoint(); + + processor->process(r); + + std::vector checkpoint2 = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint2.size(), 1); + ASSERT_EQ( + nostd::get>>(checkpoint2[0].GetAggregator()) + ->get_checkpoint(), + aggregator2->get_checkpoint()); +} + +TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulGauge) +{ + auto processor = std::unique_ptr( + new metric_sdk::UngroupedMetricsProcessor(true)); + + auto aggregator = std::shared_ptr>( + new metric_sdk::GaugeAggregator(metrics_api::InstrumentKind::Counter)); + + aggregator->update(1.1); + aggregator->update(2.2); + aggregator->checkpoint(); + + metric_sdk::Record r("name", "description", "labels", aggregator); + + processor->process(r); + + std::vector checkpoint = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint[0].GetName(), "name"); + ASSERT_EQ(checkpoint[0].GetLabels(), "labels"); + ASSERT_EQ(checkpoint[0].GetDescription(), "description"); + ASSERT_EQ( + nostd::get>>(checkpoint[0].GetAggregator()) + ->get_checkpoint(), + aggregator->get_checkpoint()); + + aggregator->update(5.4); + aggregator->checkpoint(); + + processor->process(r); + + std::vector checkpoint2 = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint2.size(), 1); + ASSERT_EQ( + nostd::get>>(checkpoint2[0].GetAggregator()) + ->get_checkpoint(), + aggregator->get_checkpoint()); +} + +TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulExact) +{ + auto processor = std::unique_ptr( + new metric_sdk::UngroupedMetricsProcessor(true)); + + auto aggregator = std::shared_ptr>( + new metric_sdk::ExactAggregator(metrics_api::InstrumentKind::Counter, false)); + + auto aggregator2 = std::shared_ptr>( + new metric_sdk::ExactAggregator(metrics_api::InstrumentKind::Counter, false)); + + aggregator->update(1.1); + aggregator->update(2.2); + aggregator2->update(1.1); + aggregator2->update(2.2); + aggregator->checkpoint(); + + metric_sdk::Record r("name", "description", "labels", aggregator); + + processor->process(r); + + std::vector checkpoint = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint[0].GetName(), "name"); + ASSERT_EQ(checkpoint[0].GetLabels(), "labels"); + ASSERT_EQ(checkpoint[0].GetDescription(), "description"); + ASSERT_EQ( + nostd::get>>(checkpoint[0].GetAggregator()) + ->get_checkpoint(), + aggregator->get_checkpoint()); + + aggregator->update(5.4); + aggregator2->update(5.4); + aggregator->checkpoint(); + aggregator2->checkpoint(); + + processor->process(r); + + std::vector checkpoint2 = processor->CheckpointSelf(); + + ASSERT_EQ(checkpoint2.size(), 1); + ASSERT_EQ( + nostd::get>>(checkpoint2[0].GetAggregator()) + ->get_checkpoint(), + aggregator2->get_checkpoint()); +} + +TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulHistogram) +{ + auto processor = std::unique_ptr( + new metric_sdk::UngroupedMetricsProcessor(true)); + + std::vector boundaries{10, 20, 30, 40, 50}; + auto aggregator = std::shared_ptr>( + new metric_sdk::HistogramAggregator(metrics_api::InstrumentKind::Counter, boundaries)); + + auto aggregator2 = std::shared_ptr>( + new metric_sdk::HistogramAggregator(metrics_api::InstrumentKind::Counter, boundaries)); + + for (int i = 0; i < 60; i++) + { + aggregator->update(i); + aggregator2->update(i); + } + aggregator->checkpoint(); + + metric_sdk::Record r("name", "description", "labels", aggregator); + + processor->process(r); + + std::vector checkpoint = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint[0].GetName(), "name"); + ASSERT_EQ(checkpoint[0].GetLabels(), "labels"); + ASSERT_EQ(checkpoint[0].GetDescription(), "description"); + ASSERT_EQ(nostd::get>>(checkpoint[0].GetAggregator()) + ->get_boundaries(), + aggregator->get_boundaries()); + ASSERT_EQ(nostd::get>>(checkpoint[0].GetAggregator()) + ->get_counts(), + aggregator->get_counts()); + + for (int i = 0; i < 60; i++) + { + aggregator->update(i); + aggregator2->update(i); + } + aggregator->checkpoint(); + aggregator2->checkpoint(); + + processor->process(r); + + std::vector checkpoint2 = processor->CheckpointSelf(); + + ASSERT_EQ(checkpoint2.size(), 1); + ASSERT_EQ(checkpoint2[0].GetName(), "name"); + ASSERT_EQ(checkpoint2[0].GetLabels(), "labels"); + ASSERT_EQ(checkpoint2[0].GetDescription(), "description"); + ASSERT_EQ(nostd::get>>(checkpoint2[0].GetAggregator()) + ->get_boundaries(), + aggregator->get_boundaries()); + ASSERT_EQ(nostd::get>>(checkpoint2[0].GetAggregator()) + ->get_counts(), + aggregator2->get_counts()); + ASSERT_EQ(nostd::get>>(checkpoint2[0].GetAggregator()) + ->get_checkpoint(), + aggregator2->get_checkpoint()); +} + +TEST(UngroupedMetricsProcessor, UngroupedProcessorKeepsRecordInformationStatefulSketch) +{ + auto processor = std::unique_ptr( + new metric_sdk::UngroupedMetricsProcessor(true)); + + auto aggregator = std::shared_ptr>( + new metric_sdk::SketchAggregator(metrics_api::InstrumentKind::Counter, .00005)); + + auto test_aggregator = std::shared_ptr>( + new metric_sdk::SketchAggregator(metrics_api::InstrumentKind::Counter, .00005)); + + for (int i = 0; i < 60; i++) + { + aggregator->update(i); + test_aggregator->update(i); + } + aggregator->checkpoint(); + + metric_sdk::Record r("name", "description", "labels", aggregator); + + processor->process(r); + + std::vector checkpoint = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint[0].GetName(), "name"); + ASSERT_EQ(checkpoint[0].GetLabels(), "labels"); + ASSERT_EQ(checkpoint[0].GetDescription(), "description"); + ASSERT_EQ(nostd::get>>(checkpoint[0].GetAggregator()) + ->get_boundaries(), + aggregator->get_boundaries()); + ASSERT_EQ(nostd::get>>(checkpoint[0].GetAggregator()) + ->get_counts(), + aggregator->get_counts()); + + for (int i = 0; i < 60; i++) + { + aggregator->update(i); + test_aggregator->update(i); + } + aggregator->checkpoint(); + test_aggregator->checkpoint(); + + processor->process(r); + + std::vector checkpoint2 = processor->CheckpointSelf(); + ASSERT_EQ(checkpoint2[0].GetName(), "name"); + ASSERT_EQ(checkpoint2[0].GetLabels(), "labels"); + ASSERT_EQ(checkpoint2[0].GetDescription(), "description"); + ASSERT_EQ(nostd::get>>(checkpoint2[0].GetAggregator()) + ->get_boundaries(), + test_aggregator->get_boundaries()); + ASSERT_EQ(nostd::get>>(checkpoint2[0].GetAggregator()) + ->get_counts(), + test_aggregator->get_counts()); + ASSERT_EQ(nostd::get>>(checkpoint2[0].GetAggregator()) + ->get_checkpoint(), + test_aggregator->get_checkpoint()); +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/BUILD new file mode 100644 index 000000000..91d56996f --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/BUILD @@ -0,0 +1,153 @@ +load("//bazel:otel_cc_benchmark.bzl", "otel_cc_benchmark") + +cc_test( + name = "random_test", + srcs = [ + "random_test.cc", + ], + tags = ["test"], + deps = [ + "//sdk/src/common:random", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "fast_random_number_generator_test", + srcs = [ + "fast_random_number_generator_test.cc", + ], + tags = ["test"], + deps = [ + "//sdk/src/common:random", + "@com_google_googletest//:gtest_main", + ], +) + +otel_cc_benchmark( + name = "random_benchmark", + srcs = ["random_benchmark.cc"], + tags = ["test"], + deps = ["//sdk/src/common:random"], +) + +cc_test( + name = "atomic_unique_ptr_test", + srcs = [ + "atomic_unique_ptr_test.cc", + ], + tags = ["test"], + deps = [ + "//api", + "//sdk:headers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "circular_buffer_range_test", + srcs = [ + "circular_buffer_range_test.cc", + ], + tags = ["test"], + deps = [ + "//api", + "//sdk:headers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "random_fork_test", + srcs = [ + "random_fork_test.cc", + ], + tags = ["test"], + deps = [ + "//sdk/src/common:random", + ], +) + +cc_library( + name = "baseline_circular_buffer", + hdrs = [ + "baseline_circular_buffer.h", + ], + include_prefix = "test/common", + deps = [ + "//api", + ], +) + +otel_cc_benchmark( + name = "circular_buffer_benchmark", + srcs = ["circular_buffer_benchmark.cc"], + tags = ["test"], + deps = [ + ":baseline_circular_buffer", + "//sdk:headers", + ], +) + +cc_test( + name = "empty_attributes_test", + srcs = [ + "empty_attributes_test.cc", + ], + tags = ["test"], + deps = [ + "//api", + "//sdk:headers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "attribute_utils_test", + srcs = [ + "attribute_utils_test.cc", + ], + tags = ["test"], + deps = [ + "//api", + "//sdk:headers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "global_log_handle_test", + srcs = [ + "global_log_handle_test.cc", + ], + tags = ["test"], + deps = [ + "//api", + "//sdk:headers", + "//sdk/src/common:global_log_handler", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "attributemap_hash_test", + srcs = [ + "attributemap_hash_test.cc", + ], + tags = ["test"], + deps = [ + "//api", + "//sdk:headers", + "@com_google_googletest//:gtest_main", + ], +) + +otel_cc_benchmark( + name = "attributemap_hash_benchmark", + srcs = ["attributemap_hash_benchmark.cc"], + tags = ["test"], + deps = [ + "//api", + "//sdk:headers", + ], +) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/CMakeLists.txt new file mode 100644 index 000000000..0fefc86b1 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/CMakeLists.txt @@ -0,0 +1,37 @@ +foreach( + testname + random_test + fast_random_number_generator_test + atomic_unique_ptr_test + circular_buffer_range_test + circular_buffer_test + attribute_utils_test + attributemap_hash_test + global_log_handle_test) + + add_executable(${testname} "${testname}.cc") + target_link_libraries( + ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} + opentelemetry_common opentelemetry_trace) + + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX trace. + TEST_LIST ${testname}) +endforeach() + +add_executable(random_fork_test random_fork_test.cc) +target_link_libraries(random_fork_test opentelemetry_common) +add_test(random_fork_test random_fork_test) + +add_executable(random_benchmark random_benchmark.cc) +target_link_libraries(random_benchmark benchmark::benchmark + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_common) + +add_executable(circular_buffer_benchmark circular_buffer_benchmark.cc) +target_link_libraries(circular_buffer_benchmark benchmark::benchmark + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) + +add_executable(attributemap_hash_benchmark attributemap_hash_benchmark.cc) +target_link_libraries(attributemap_hash_benchmark benchmark::benchmark + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_common) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/atomic_unique_ptr_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/atomic_unique_ptr_test.cc new file mode 100644 index 000000000..aa6d88a00 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/atomic_unique_ptr_test.cc @@ -0,0 +1,42 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/common/atomic_unique_ptr.h" + +#include +using opentelemetry::sdk::common::AtomicUniquePtr; + +TEST(AtomicUniquePtrTest, SwapIfNullWithNull) +{ + AtomicUniquePtr ptr; + EXPECT_TRUE(ptr.IsNull()); + + std::unique_ptr x{new int{33}}; + EXPECT_TRUE(ptr.SwapIfNull(x)); + EXPECT_EQ(x, nullptr); +} + +TEST(AtomicUniquePtrTest, SwapIfNullWithNonNull) +{ + AtomicUniquePtr ptr; + ptr.Reset(new int{11}); + std::unique_ptr x{new int{33}}; + EXPECT_TRUE(!ptr.SwapIfNull(x)); + EXPECT_NE(x, nullptr); + EXPECT_EQ(*x, 33); + EXPECT_EQ(*ptr, 11); +} + +TEST(AtomicUniquePtrTest, Swap) +{ + AtomicUniquePtr ptr; + EXPECT_TRUE(ptr.IsNull()); + + ptr.Reset(new int{11}); + std::unique_ptr x{new int{33}}; + ptr.Swap(x); + EXPECT_FALSE(ptr.IsNull()); + EXPECT_NE(x, nullptr); + EXPECT_EQ(*x, 11); + EXPECT_EQ(*ptr, 33); +} diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attribute_utils_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attribute_utils_test.cc new file mode 100644 index 000000000..b7ef17244 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attribute_utils_test.cc @@ -0,0 +1,53 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/common/attribute_utils.h" + +#include + +TEST(AttributeMapTest, DefaultConstruction) +{ + + opentelemetry::sdk::common::AttributeMap attribute_map; + EXPECT_EQ(attribute_map.GetAttributes().size(), 0); +} + +TEST(OrderedAttributeMapTest, DefaultConstruction) +{ + opentelemetry::sdk::common::OrderedAttributeMap attribute_map; + EXPECT_EQ(attribute_map.GetAttributes().size(), 0); +} + +TEST(AttributeMapTest, AttributesConstruction) +{ + const int kNumAttributes = 3; + std::string keys[kNumAttributes] = {"attr1", "attr2", "attr3"}; + int values[kNumAttributes] = {15, 24, 37}; + std::map attributes = { + {keys[0], values[0]}, {keys[1], values[1]}, {keys[2], values[2]}}; + + opentelemetry::common::KeyValueIterableView> iterable(attributes); + opentelemetry::sdk::common::AttributeMap attribute_map(iterable); + + for (int i = 0; i < kNumAttributes; i++) + { + EXPECT_EQ(opentelemetry::nostd::get(attribute_map.GetAttributes().at(keys[i])), values[i]); + } +} + +TEST(OrderedAttributeMapTest, AttributesConstruction) +{ + const int kNumAttributes = 3; + std::string keys[kNumAttributes] = {"attr1", "attr2", "attr3"}; + int values[kNumAttributes] = {15, 24, 37}; + std::map attributes = { + {keys[0], values[0]}, {keys[1], values[1]}, {keys[2], values[2]}}; + + opentelemetry::common::KeyValueIterableView> iterable(attributes); + opentelemetry::sdk::common::OrderedAttributeMap attribute_map(iterable); + + for (int i = 0; i < kNumAttributes; i++) + { + EXPECT_EQ(opentelemetry::nostd::get(attribute_map.GetAttributes().at(keys[i])), values[i]); + } +} diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attributemap_hash_benchmark.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attributemap_hash_benchmark.cc new file mode 100644 index 000000000..811ecb23d --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attributemap_hash_benchmark.cc @@ -0,0 +1,22 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include +#include "opentelemetry/sdk/common/attributemap_hash.h" + +using namespace opentelemetry::sdk::common; +namespace +{ +void BM_AttributeMapHash(benchmark::State &state) +{ + OrderedAttributeMap map1 = {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}, {"k4", "v4"}, + {"k5", true}, {"k6", 12}, {"k7", 12.209}}; + while (state.KeepRunning()) + { + benchmark::DoNotOptimize(GetHashForAttributeMap(map1)); + } +} +BENCHMARK(BM_AttributeMapHash); + +} // namespace +BENCHMARK_MAIN(); diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attributemap_hash_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attributemap_hash_test.cc new file mode 100644 index 000000000..7d2748670 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/attributemap_hash_test.cc @@ -0,0 +1,32 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/common/attributemap_hash.h" +#include + +using namespace opentelemetry::sdk::common; +TEST(AttributeMapHashTest, BasicTests) +{ + { + OrderedAttributeMap map1 = {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}}; + OrderedAttributeMap map2 = {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}, {"k4", "v4"}}; + OrderedAttributeMap map3 = {{"k3", "v3"}, {"k1", "v1"}, {"k2", "v2"}}; + + EXPECT_TRUE(GetHashForAttributeMap(map1) != 0); + EXPECT_TRUE(GetHashForAttributeMap(map1) == GetHashForAttributeMap(map1)); + EXPECT_TRUE(GetHashForAttributeMap(map1) != GetHashForAttributeMap(map2)); + EXPECT_TRUE(GetHashForAttributeMap(map1) == GetHashForAttributeMap(map3)); + } + + { + OrderedAttributeMap map1 = {{"k1", 10}, {"k2", true}, {"k3", 12.22}}; + OrderedAttributeMap map2 = {{"k3", 12.22}, {"k1", 10}, {"k2", true}}; + EXPECT_TRUE(GetHashForAttributeMap(map1) == GetHashForAttributeMap(map2)); + EXPECT_TRUE(GetHashForAttributeMap(map1) != 0); + } + + { + OrderedAttributeMap map1 = {}; + EXPECT_TRUE(GetHashForAttributeMap(map1) == 0); + } +} \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/baseline_circular_buffer.h b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/baseline_circular_buffer.h new file mode 100644 index 000000000..398a4d038 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/baseline_circular_buffer.h @@ -0,0 +1,88 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include +#include + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace testing +{ +/** + * A locking circular buffer. + * + * Used as a baseline in benchmarking. + */ +template +class BaselineCircularBuffer +{ +public: + explicit BaselineCircularBuffer(size_t max_size) : data_{max_size} {} + + /** + * Add an element to the circular buffer. + * @param element the element to add + * @return true if the element was added successfully + */ + bool Add(std::unique_ptr &element) noexcept { return this->Add(std::move(element)); } + + bool Add(std::unique_ptr &&element) noexcept + { + std::lock_guard lock_guard{mutex_}; + if (tail_ + data_.size() == head_) + { + return false; + } + data_[head_ % data_.size()] = std::move(element); + head_ += 1; + return true; + } + + /** + * Consume elements in the circular buffer. + * @param f the callback to call for each element + */ + template + void Consume(F f) noexcept + { + std::lock_guard lock_guard{mutex_}; + if (head_ == tail_) + { + return; + } + auto tail_index = tail_ % data_.size(); + auto head_index = head_ % data_.size(); + if (tail_index < head_index) + { + for (auto i = tail_index; i < head_index; ++i) + { + f(std::move(data_[i])); + } + } + else + { + for (auto i = tail_index; i < data_.size(); ++i) + { + f(std::move(data_[i])); + } + for (auto i = 0ull; i < head_index; ++i) + { + f(std::move(data_[i])); + } + } + tail_ = head_; + } + +private: + std::mutex mutex_; + uint64_t head_{0}; + uint64_t tail_{0}; + std::vector> data_; +}; +} // namespace testing +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_benchmark.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_benchmark.cc new file mode 100644 index 000000000..1f2b9b42c --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_benchmark.cc @@ -0,0 +1,133 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "benchmark/benchmark.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "opentelemetry/sdk/common/circular_buffer.h" +#include "test/common/baseline_circular_buffer.h" +using opentelemetry::sdk::common::AtomicUniquePtr; +using opentelemetry::sdk::common::CircularBuffer; +using opentelemetry::sdk::common::CircularBufferRange; +using opentelemetry::testing::BaselineCircularBuffer; + +const int N = 10000; + +static uint64_t ConsumeBufferNumbers(BaselineCircularBuffer &buffer) noexcept +{ + uint64_t result = 0; + buffer.Consume([&](std::unique_ptr &&x) { + result += *x; + x.reset(); + }); + return result; +} + +static uint64_t ConsumeBufferNumbers(CircularBuffer &buffer) noexcept +{ + uint64_t result = 0; + buffer.Consume(buffer.size(), + [&](CircularBufferRange> &range) noexcept { + range.ForEach([&](AtomicUniquePtr &ptr) noexcept { + result += *ptr; + ptr.Reset(); + return true; + }); + }); + return result; +} + +template +static void GenerateNumbersForThread(Buffer &buffer, int n, std::atomic &sum) noexcept +{ + thread_local std::mt19937_64 random_number_generator{std::random_device{}()}; + for (int i = 0; i < n; ++i) + { + auto x = random_number_generator(); + std::unique_ptr element{new uint64_t{x}}; + if (buffer.Add(element)) + { + sum += x; + } + } +} + +template +static uint64_t GenerateNumbers(Buffer &buffer, int num_threads, int n) noexcept +{ + std::atomic sum{0}; + std::vector threads(num_threads); + for (auto &thread : threads) + { + thread = std::thread{GenerateNumbersForThread, std::ref(buffer), n, std::ref(sum)}; + } + for (auto &thread : threads) + { + thread.join(); + } + return sum; +} + +template +static void ConsumeNumbers(Buffer &buffer, uint64_t &sum, std::atomic &finished) noexcept +{ + while (!finished) + { + sum += ConsumeBufferNumbers(buffer); + } + sum += ConsumeBufferNumbers(buffer); +} + +template +static void RunSimulation(Buffer &buffer, int num_threads, int n) noexcept +{ + std::atomic finished{false}; + uint64_t consumer_sum{0}; + std::thread consumer_thread{ConsumeNumbers, std::ref(buffer), std::ref(consumer_sum), + std::ref(finished)}; + uint64_t producer_sum = GenerateNumbers(buffer, num_threads, n); + finished = true; + consumer_thread.join(); + if (consumer_sum != producer_sum) + { + std::cerr << "Sumulation failed: consumer_sum != producer_sum\n"; + std::terminate(); + } +} + +static void BM_BaselineBuffer(benchmark::State &state) +{ + const size_t max_elements = 500; + const int num_threads = static_cast(state.range(0)); + const int n = static_cast(N / num_threads); + BaselineCircularBuffer buffer{max_elements}; + for (auto _ : state) + { + RunSimulation(buffer, num_threads, n); + } +} + +BENCHMARK(BM_BaselineBuffer)->Arg(1)->Arg(2)->Arg(4); + +static void BM_LockFreeBuffer(benchmark::State &state) +{ + const size_t max_elements = 500; + const int num_threads = static_cast(state.range(0)); + const int n = static_cast(N / num_threads); + CircularBuffer buffer{max_elements}; + for (auto _ : state) + { + RunSimulation(buffer, num_threads, n); + } +} + +BENCHMARK(BM_LockFreeBuffer)->Arg(1)->Arg(2)->Arg(4); + +BENCHMARK_MAIN(); diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_range_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_range_test.cc new file mode 100644 index 000000000..585270468 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_range_test.cc @@ -0,0 +1,59 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/common/circular_buffer_range.h" + +#include + +#include +using opentelemetry::sdk::common::CircularBufferRange; + +TEST(CircularBufferRangeTest, ForEach) +{ + int array1[] = {1, 2, 3, 4}; + int array2[] = {5, 6, 7}; + CircularBufferRange range{array1, array2}; + + int x = 0; + range.ForEach([&](int y) { + EXPECT_EQ(++x, y); + return true; + }); + EXPECT_EQ(x, 7); +} + +TEST(CircularBufferRangeTest, ForEachWithExit) +{ + int array1[] = {1, 2, 3, 4}; + int array2[] = {5, 6, 7}; + CircularBufferRange range{array1, array2}; + + int x = 0; + range.ForEach([&](int y) { + EXPECT_EQ(++x, y); + return false; + }); + EXPECT_EQ(x, 1); + + x = 0; + range.ForEach([&](int y) { + EXPECT_EQ(++x, y); + return y != 5; + }); + EXPECT_EQ(x, 5); +} + +TEST(CircularBufferRangeTest, Conversion) +{ + int array1[] = {1, 2, 3, 4}; + int array2[] = {5, 6, 7}; + CircularBufferRange range{array1, array2}; + + CircularBufferRange range2{range}; + int x = 0; + range2.ForEach([&](int y) { + EXPECT_EQ(++x, y); + return true; + }); + EXPECT_EQ(x, 7); +} diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_test.cc new file mode 100644 index 000000000..a20c3e42a --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/circular_buffer_test.cc @@ -0,0 +1,161 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/common/circular_buffer.h" + +#include +#include +#include +#include + +#include +using opentelemetry::sdk::common::AtomicUniquePtr; +using opentelemetry::sdk::common::CircularBuffer; +using opentelemetry::sdk::common::CircularBufferRange; + +static thread_local std::mt19937 RandomNumberGenerator{std::random_device{}()}; + +static void GenerateRandomNumbers(CircularBuffer &buffer, + std::vector &numbers, + int n) +{ + for (int i = 0; i < n; ++i) + { + auto value = static_cast(RandomNumberGenerator()); + std::unique_ptr x{new uint32_t{value}}; + if (buffer.Add(x)) + { + numbers.push_back(value); + } + } +} + +static void RunNumberProducers(CircularBuffer &buffer, + std::vector &numbers, + int num_threads, + int n) +{ + std::vector> thread_numbers(num_threads); + std::vector threads(num_threads); + for (int thread_index = 0; thread_index < num_threads; ++thread_index) + { + threads[thread_index] = std::thread{GenerateRandomNumbers, std::ref(buffer), + std::ref(thread_numbers[thread_index]), n}; + } + for (auto &thread : threads) + { + thread.join(); + } + for (int thread_index = 0; thread_index < num_threads; ++thread_index) + { + numbers.insert(numbers.end(), thread_numbers[thread_index].begin(), + thread_numbers[thread_index].end()); + } +} + +void RunNumberConsumer(CircularBuffer &buffer, + std::atomic &exit, + std::vector &numbers) +{ + while (true) + { + if (exit && buffer.Peek().empty()) + { + return; + } + auto n = std::uniform_int_distribution{0, buffer.Peek().size()}(RandomNumberGenerator); + buffer.Consume(n, [&](CircularBufferRange> range) noexcept { + assert(range.size() == n); + range.ForEach([&](AtomicUniquePtr &ptr) noexcept { + assert(!ptr.IsNull()); + numbers.push_back(*ptr); + ptr.Reset(); + return true; + }); + }); + } +} + +TEST(CircularBufferTest, Add) +{ + CircularBuffer buffer{10}; + + std::unique_ptr x{new int{11}}; + EXPECT_TRUE(buffer.Add(x)); + EXPECT_EQ(x, nullptr); + auto range = buffer.Peek(); + EXPECT_EQ(range.size(), 1); + range.ForEach([](const AtomicUniquePtr &y) { + EXPECT_EQ(*y, 11); + return true; + }); +} + +TEST(CircularBufferTest, Clear) +{ + CircularBuffer buffer{10}; + + std::unique_ptr x{new int{11}}; + EXPECT_TRUE(buffer.Add(x)); + EXPECT_EQ(x, nullptr); + buffer.Clear(); + EXPECT_TRUE(buffer.empty()); +} + +TEST(CircularBufferTest, AddOnFull) +{ + CircularBuffer buffer{10}; + for (int i = 0; i < static_cast(buffer.max_size()); ++i) + { + std::unique_ptr x{new int{i}}; + EXPECT_TRUE(buffer.Add(x)); + } + std::unique_ptr x{new int{33}}; + EXPECT_FALSE(buffer.Add(x)); + EXPECT_NE(x, nullptr); + EXPECT_EQ(*x, 33); +} + +TEST(CircularBufferTest, Consume) +{ + CircularBuffer buffer{10}; + for (int i = 0; i < static_cast(buffer.max_size()); ++i) + { + std::unique_ptr x{new int{i}}; + EXPECT_TRUE(buffer.Add(x)); + } + int count = 0; + buffer.Consume(5, [&](CircularBufferRange> range) noexcept { + range.ForEach([&](AtomicUniquePtr &ptr) { + EXPECT_EQ(*ptr, count++); + ptr.Reset(); + return true; + }); + }); + EXPECT_EQ(count, 5); +} + +TEST(CircularBufferTest, Simulation) +{ + const int num_producer_threads = 4; + const int n = 25000; + for (size_t max_size : {1, 2, 10, 50, 100, 1000}) + { + CircularBuffer buffer{max_size}; + std::vector producer_numbers; + std::vector consumer_numbers; + auto producers = std::thread{RunNumberProducers, std::ref(buffer), std::ref(producer_numbers), + num_producer_threads, n}; + std::atomic exit{false}; + auto consumer = std::thread{RunNumberConsumer, std::ref(buffer), std::ref(exit), + std::ref(consumer_numbers)}; + producers.join(); + exit = true; + consumer.join(); + std::sort(producer_numbers.begin(), producer_numbers.end()); + std::sort(consumer_numbers.begin(), consumer_numbers.end()); + + EXPECT_EQ(producer_numbers.size(), consumer_numbers.size()); + EXPECT_EQ(producer_numbers, consumer_numbers); + } +} diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/empty_attributes_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/empty_attributes_test.cc new file mode 100644 index 000000000..f37ea0a5c --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/empty_attributes_test.cc @@ -0,0 +1,19 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/common/empty_attributes.h" + +#include + +TEST(EmptyAttributesTest, TestSize) +{ + EXPECT_EQ(opentelemetry::sdk::GetEmptyAttributes().size(), 0); +} + +// Test that GetEmptyAttributes() always returns the same KeyValueIterableView +TEST(EmptyAttributesTest, TestMemory) +{ + auto attributes1 = opentelemetry::sdk::GetEmptyAttributes(); + auto attributes2 = opentelemetry::sdk::GetEmptyAttributes(); + EXPECT_EQ(memcmp(&attributes1, &attributes2, sizeof(attributes1)), 0); +} diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/fast_random_number_generator_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/fast_random_number_generator_test.cc new file mode 100644 index 000000000..e9209fdb6 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/fast_random_number_generator_test.cc @@ -0,0 +1,22 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "src/common/random.h" + +#include + +#include + +using opentelemetry::sdk::common::FastRandomNumberGenerator; + +TEST(FastRandomNumberGeneratorTest, GenerateUniqueNumbers) +{ + std::seed_seq seed_sequence{1, 2, 3}; + FastRandomNumberGenerator random_number_generator; + random_number_generator.seed(seed_sequence); + std::set values; + for (int i = 0; i < 1000; ++i) + { + EXPECT_TRUE(values.insert(random_number_generator()).second); + } +} diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/global_log_handle_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/global_log_handle_test.cc new file mode 100644 index 000000000..a38bdc872 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/global_log_handle_test.cc @@ -0,0 +1,67 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/common/global_log_handler.h" + +#include + +#include + +class CustomLogHandler : public opentelemetry::sdk::common::internal_log::LogHandler +{ +public: + void Handle(opentelemetry::sdk::common::internal_log::LogLevel level, + const char *, + int, + const char *msg, + const opentelemetry::sdk::common::AttributeMap &) noexcept override + { + if (level == opentelemetry::sdk::common::internal_log::LogLevel::Debug) + { + EXPECT_EQ(0, strncmp(msg, "Debug message", 13)); + } + else if (level == opentelemetry::sdk::common::internal_log::LogLevel::Error) + { + EXPECT_EQ(0, strncmp(msg, "Error message", 13)); + } + else if (level == opentelemetry::sdk::common::internal_log::LogLevel::Info) + { + EXPECT_EQ(0, strncmp(msg, "Info message", 12)); + } + else if (level == opentelemetry::sdk::common::internal_log::LogLevel::Warning) + { + EXPECT_EQ(0, strncmp(msg, "Warning message", 15)); + } + ++count; + } + + size_t count = 0; +}; + +TEST(GlobalLogHandleTest, CustomLogHandler) +{ + using opentelemetry::sdk::common::internal_log::LogHandler; + auto backup_log_handle = + opentelemetry::sdk::common::internal_log::GlobalLogHandler::GetLogHandler(); + auto backup_log_level = opentelemetry::sdk::common::internal_log::GlobalLogHandler::GetLogLevel(); + + auto custom_log_handler = opentelemetry::nostd::shared_ptr(new CustomLogHandler{}); + opentelemetry::sdk::common::internal_log::GlobalLogHandler::SetLogHandler(custom_log_handler); + auto before_count = static_cast(custom_log_handler.get())->count; + opentelemetry::sdk::common::AttributeMap attributes = { + {"url", "https://opentelemetry.io/"}, {"content-length", 0}, {"content-type", "text/html"}}; + OTEL_INTERNAL_LOG_ERROR("Error message"); + OTEL_INTERNAL_LOG_DEBUG("Debug message. Headers:", attributes); + EXPECT_EQ(before_count + 1, static_cast(custom_log_handler.get())->count); + + opentelemetry::sdk::common::internal_log::GlobalLogHandler::SetLogLevel( + opentelemetry::sdk::common::internal_log::LogLevel::Debug); + OTEL_INTERNAL_LOG_ERROR("Error message"); + OTEL_INTERNAL_LOG_DEBUG("Debug message. Headers:", attributes); + OTEL_INTERNAL_LOG_INFO("Info message"); + OTEL_INTERNAL_LOG_WARN("Warning message"); + EXPECT_EQ(before_count + 5, static_cast(custom_log_handler.get())->count); + + opentelemetry::sdk::common::internal_log::GlobalLogHandler::SetLogHandler(backup_log_handle); + opentelemetry::sdk::common::internal_log::GlobalLogHandler::SetLogLevel(backup_log_level); +} diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_benchmark.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_benchmark.cc new file mode 100644 index 000000000..df2ebf95e --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_benchmark.cc @@ -0,0 +1,35 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "src/common/random.h" + +#include +#include + +#include + +namespace +{ +using opentelemetry::sdk::common::Random; + +void BM_RandomIdGeneration(benchmark::State &state) +{ + while (state.KeepRunning()) + { + benchmark::DoNotOptimize(Random::GenerateRandom64()); + } +} +BENCHMARK(BM_RandomIdGeneration); + +void BM_RandomIdStdGeneration(benchmark::State &state) +{ + std::mt19937_64 generator{0}; + while (state.KeepRunning()) + { + benchmark::DoNotOptimize(generator()); + } +} +BENCHMARK(BM_RandomIdStdGeneration); + +} // namespace +BENCHMARK_MAIN(); diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_fork_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_fork_test.cc new file mode 100644 index 000000000..2db8b9fcd --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_fork_test.cc @@ -0,0 +1,53 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef __unix__ +// Verifies that IDs don't clash after forking the process. +// +// See https://github.com/opentracing-contrib/nginx-opentracing/issues/52 +# include "src/common/random.h" + +# include +# include +# include +# include +# include +# include +# include +using opentelemetry::sdk::common::Random; + +static uint64_t *child_id; + +int main() +{ + // Set up shared memory to communicate between parent and child processes. + // + // See https://stackoverflow.com/a/13274800/4447365 + child_id = static_cast( + mmap(nullptr, sizeof(*child_id), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0)); + *child_id = 0; + if (fork() == 0) + { + *child_id = Random::GenerateRandom64(); + exit(EXIT_SUCCESS); + } + else + { + wait(nullptr); + auto parent_id = Random::GenerateRandom64(); + auto child_id_copy = *child_id; + munmap(static_cast(child_id), sizeof(*child_id)); + if (parent_id == child_id_copy) + { + std::cerr << "Child and parent ids are the same value " << parent_id << "\n"; + return -1; + } + } + return 0; +} +#else +int main() +{ + return 0; +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_test.cc new file mode 100644 index 000000000..35cfd4a1e --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/common/random_test.cc @@ -0,0 +1,35 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "src/common/random.h" + +#include +#include + +#include +using opentelemetry::sdk::common::Random; + +TEST(RandomTest, GenerateRandom64) +{ + EXPECT_NE(Random::GenerateRandom64(), Random::GenerateRandom64()); +} + +TEST(RandomTest, GenerateRandomBuffer) +{ + uint8_t buf1[8] = {0}; + uint8_t buf2[8] = {0}; + Random::GenerateRandomBuffer(buf1); + Random::GenerateRandomBuffer(buf2); + EXPECT_FALSE(std::equal(std::begin(buf1), std::end(buf1), std::begin(buf2))); + + // Edge cases. + for (auto size : {7, 8, 9, 16, 17}) + { + std::vector buf1(size); + std::vector buf2(size); + + Random::GenerateRandomBuffer(buf1); + Random::GenerateRandomBuffer(buf2); + EXPECT_FALSE(std::equal(std::begin(buf1), std::end(buf1), std::begin(buf2))); + } +} diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/BUILD new file mode 100644 index 000000000..38cc25300 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/BUILD @@ -0,0 +1,14 @@ +load("//bazel:otel_cc_benchmark.bzl", "otel_cc_benchmark") + +cc_test( + name = "instrumentationlibrary_test", + srcs = [ + "instrumentationlibrary_test.cc", + ], + tags = ["test"], + deps = [ + "//api", + "//sdk:headers", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/CMakeLists.txt new file mode 100644 index 000000000..512266dc8 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/CMakeLists.txt @@ -0,0 +1,11 @@ +include(GoogleTest) + +foreach(testname instrumentationlibrary_test) + add_executable(${testname} "${testname}.cc") + target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX instrumentationlibrary. + TEST_LIST ${testname}) +endforeach() diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/instrumentationlibrary_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/instrumentationlibrary_test.cc new file mode 100644 index 000000000..a410ca99f --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/instrumentationlibrary/instrumentationlibrary_test.cc @@ -0,0 +1,26 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h" + +#include +#include +#include + +using namespace opentelemetry; +using namespace opentelemetry::sdk::instrumentationlibrary; + +TEST(InstrumentationLibrary, CreateInstrumentationLibrary) +{ + + std::string library_name = "opentelemetry-cpp"; + std::string library_version = "0.1.0"; + std::string schema_url = "https://opentelemetry.io/schemas/1.2.0"; + auto instrumentation_library = + InstrumentationLibrary::Create(library_name, library_version, schema_url); + + EXPECT_EQ(instrumentation_library->GetName(), library_name); + EXPECT_EQ(instrumentation_library->GetVersion(), library_version); + EXPECT_EQ(instrumentation_library->GetSchemaURL(), schema_url); +} diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/BUILD new file mode 100644 index 000000000..c8f051070 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/BUILD @@ -0,0 +1,75 @@ +cc_test( + name = "logger_provider_sdk_test", + srcs = [ + "logger_provider_sdk_test.cc", + ], + tags = [ + "logs", + "test", + ], + deps = [ + "//api", + "//sdk/src/logs", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "logger_sdk_test", + srcs = [ + "logger_sdk_test.cc", + ], + tags = [ + "logs", + "test", + ], + deps = [ + "//sdk/src/logs", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "simple_log_processor_test", + srcs = [ + "simple_log_processor_test.cc", + ], + tags = [ + "logs", + "test", + ], + deps = [ + "//sdk/src/logs", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "log_record_test", + srcs = [ + "log_record_test.cc", + ], + tags = [ + "logs", + "test", + ], + deps = [ + "//sdk/src/logs", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "batch_log_processor_test", + srcs = [ + "batch_log_processor_test.cc", + ], + tags = [ + "logs", + "test", + ], + deps = [ + "//sdk/src/logs", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/CMakeLists.txt new file mode 100644 index 000000000..84b865d22 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/CMakeLists.txt @@ -0,0 +1,10 @@ +foreach(testname logger_provider_sdk_test logger_sdk_test log_record_test + simple_log_processor_test batch_log_processor_test) + add_executable(${testname} "${testname}.cc") + target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_logs) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX logs. + TEST_LIST ${testname}) +endforeach() diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/batch_log_processor_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/batch_log_processor_test.cc new file mode 100644 index 000000000..63e44676c --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/batch_log_processor_test.cc @@ -0,0 +1,269 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/logs/batch_log_processor.h" +# include "opentelemetry/sdk/logs/exporter.h" +# include "opentelemetry/sdk/logs/log_record.h" + +# include +# include +# include + +using namespace opentelemetry::sdk::logs; +using namespace opentelemetry::sdk::common; + +/** + * A sample log exporter + * for testing the batch log processor + */ +class MockLogExporter final : public LogExporter +{ +public: + MockLogExporter(std::shared_ptr>> logs_received, + std::shared_ptr> is_shutdown, + std::shared_ptr> is_export_completed, + const std::chrono::milliseconds export_delay = std::chrono::milliseconds(0)) + : logs_received_(logs_received), + is_shutdown_(is_shutdown), + is_export_completed_(is_export_completed), + export_delay_(export_delay) + {} + + std::unique_ptr MakeRecordable() noexcept + { + return std::unique_ptr(new LogRecord()); + } + + // Export method stores the logs received into a shared list of record names + ExportResult Export( + const opentelemetry::nostd::span> &records) noexcept override + { + *is_export_completed_ = false; // Meant exclusively to test scheduled_delay_millis + + for (auto &record : records) + { + auto log = std::unique_ptr(static_cast(record.release())); + if (log != nullptr) + { + logs_received_->push_back(std::move(log)); + } + } + + *is_export_completed_ = true; + return ExportResult::kSuccess; + } + + // toggles the boolean flag marking this exporter as shut down + bool Shutdown( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override + { + *is_shutdown_ = true; + return true; + } + +private: + std::shared_ptr>> logs_received_; + std::shared_ptr> is_shutdown_; + std::shared_ptr> is_export_completed_; + const std::chrono::milliseconds export_delay_; +}; + +/** + * A fixture class for testing the BatchLogProcessor class that uses the TestExporter defined above. + */ +class BatchLogProcessorTest : public testing::Test // ::testing::Test +{ +public: + // returns a batch log processor that received a batch of log records, a shared pointer to a + // is_shutdown flag, and the processor configuration options (default if unspecified) + std::shared_ptr GetMockProcessor( + std::shared_ptr>> logs_received, + std::shared_ptr> is_shutdown, + std::shared_ptr> is_export_completed = + std::shared_ptr>(new std::atomic(false)), + const std::chrono::milliseconds export_delay = std::chrono::milliseconds(0), + const std::chrono::milliseconds scheduled_delay_millis = std::chrono::milliseconds(5000), + const size_t max_queue_size = 2048, + const size_t max_export_batch_size = 512) + { + return std::shared_ptr( + new BatchLogProcessor(std::unique_ptr(new MockLogExporter( + logs_received, is_shutdown, is_export_completed, export_delay)), + max_queue_size, scheduled_delay_millis, max_export_batch_size)); + } +}; + +TEST_F(BatchLogProcessorTest, TestShutdown) +{ + // initialize a batch log processor with the test exporter + std::shared_ptr>> logs_received( + new std::vector>); + std::shared_ptr> is_shutdown(new std::atomic(false)); + + auto batch_processor = GetMockProcessor(logs_received, is_shutdown); + + // Create a few test log records and send them to the processor + const int num_logs = 3; + + for (int i = 0; i < num_logs; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetBody("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + + // Test that shutting down the processor will first wait for the + // current batch of logs to be sent to the log exporter + // by checking the number of logs sent and the names of the logs sent + EXPECT_EQ(true, batch_processor->Shutdown()); + // It's safe to shutdown again + EXPECT_TRUE(batch_processor->Shutdown()); + + EXPECT_EQ(num_logs, logs_received->size()); + + // Assume logs are received by exporter in same order as sent by processor + for (int i = 0; i < num_logs; ++i) + { + EXPECT_EQ("Log" + std::to_string(i), logs_received->at(i)->GetBody()); + } + + // Also check that the processor is shut down at the end + EXPECT_TRUE(is_shutdown->load()); +} + +TEST_F(BatchLogProcessorTest, TestForceFlush) +{ + std::shared_ptr> is_shutdown(new std::atomic(false)); + std::shared_ptr>> logs_received( + new std::vector>); + + auto batch_processor = GetMockProcessor(logs_received, is_shutdown); + const int num_logs = 2048; + + for (int i = 0; i < num_logs; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetBody("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + + EXPECT_TRUE(batch_processor->ForceFlush()); + + EXPECT_EQ(num_logs, logs_received->size()); + for (int i = 0; i < num_logs; ++i) + { + EXPECT_EQ("Log" + std::to_string(i), logs_received->at(i)->GetBody()); + } + + // Create some more logs to make sure that the processor still works + for (int i = 0; i < num_logs; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetBody("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + + EXPECT_TRUE(batch_processor->ForceFlush()); + + EXPECT_EQ(num_logs * 2, logs_received->size()); + for (int i = 0; i < num_logs * 2; ++i) + { + EXPECT_EQ("Log" + std::to_string(i % num_logs), logs_received->at(i)->GetBody()); + } +} + +TEST_F(BatchLogProcessorTest, TestManyLogsLoss) +{ + /* Test that when exporting more than max_queue_size logs, some are most likely lost*/ + + std::shared_ptr> is_shutdown(new std::atomic(false)); + std::shared_ptr>> logs_received( + new std::vector>); + + const int max_queue_size = 4096; + + auto batch_processor = GetMockProcessor(logs_received, is_shutdown); + + // Create max_queue_size log records + for (int i = 0; i < max_queue_size; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetBody("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + + EXPECT_TRUE(batch_processor->ForceFlush()); + + // Log should be exported by now + EXPECT_GE(max_queue_size, logs_received->size()); +} + +TEST_F(BatchLogProcessorTest, TestManyLogsLossLess) +{ + /* Test that no logs are lost when sending max_queue_size logs */ + + std::shared_ptr> is_shutdown(new std::atomic(false)); + std::shared_ptr>> logs_received( + new std::vector>); + auto batch_processor = GetMockProcessor(logs_received, is_shutdown); + + const int num_logs = 2048; + + for (int i = 0; i < num_logs; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetBody("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + + EXPECT_TRUE(batch_processor->ForceFlush()); + + EXPECT_EQ(num_logs, logs_received->size()); + for (int i = 0; i < num_logs; ++i) + { + EXPECT_EQ("Log" + std::to_string(i), logs_received->at(i)->GetBody()); + } +} + +TEST_F(BatchLogProcessorTest, TestScheduledDelayMillis) +{ + /* Test that max_export_batch_size logs are exported every scheduled_delay_millis + seconds */ + + std::shared_ptr> is_shutdown(new std::atomic(false)); + std::shared_ptr> is_export_completed(new std::atomic(false)); + std::shared_ptr>> logs_received( + new std::vector>); + + const std::chrono::milliseconds export_delay(0); + const std::chrono::milliseconds scheduled_delay_millis(2000); + const size_t max_export_batch_size = 512; + + auto batch_processor = GetMockProcessor(logs_received, is_shutdown, is_export_completed, + export_delay, scheduled_delay_millis); + + for (std::size_t i = 0; i < max_export_batch_size; ++i) + { + auto log = batch_processor->MakeRecordable(); + log->SetBody("Log" + std::to_string(i)); + batch_processor->OnReceive(std::move(log)); + } + // Sleep for scheduled_delay_millis milliseconds + std::this_thread::sleep_for(scheduled_delay_millis); + + // small delay to give time to export, which is being performed + // asynchronously by the worker thread (this thread will not + // forcibly join() the main thread unless processor's shutdown() is called). + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + // Logs should be exported by now + EXPECT_TRUE(is_export_completed->load()); + EXPECT_EQ(max_export_batch_size, logs_received->size()); + for (size_t i = 0; i < max_export_batch_size; ++i) + { + EXPECT_EQ("Log" + std::to_string(i), logs_received->at(i)->GetBody()); + } +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/log_record_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/log_record_test.cc new file mode 100644 index 000000000..89b07473a --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/log_record_test.cc @@ -0,0 +1,66 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/logs/log_record.h" +# include "opentelemetry/nostd/variant.h" +# include "opentelemetry/trace/span_id.h" +# include "opentelemetry/trace/trace_id.h" + +# include + +using opentelemetry::sdk::logs::LogRecord; +namespace trace_api = opentelemetry::trace; +namespace logs_api = opentelemetry::logs; +namespace nostd = opentelemetry::nostd; + +// Test what a default LogRecord with no fields set holds +TEST(LogRecord, GetDefaultValues) +{ + trace_api::TraceId zero_trace_id; + trace_api::SpanId zero_span_id; + trace_api::TraceFlags zero_trace_flags; + LogRecord record; + + ASSERT_EQ(record.GetSeverity(), logs_api::Severity::kInvalid); + ASSERT_EQ(record.GetBody(), ""); + ASSERT_NE(record.GetResource().GetAttributes().size(), 0); + ASSERT_EQ(record.GetAttributes().size(), 0); + ASSERT_EQ(record.GetTraceId(), zero_trace_id); + ASSERT_EQ(record.GetSpanId(), zero_span_id); + ASSERT_EQ(record.GetTraceFlags(), zero_trace_flags); + ASSERT_EQ(record.GetTimestamp().time_since_epoch(), std::chrono::nanoseconds(0)); +} + +// Test LogRecord fields are properly set and get +TEST(LogRecord, SetAndGet) +{ + trace_api::TraceId trace_id; + trace_api::SpanId span_id; + trace_api::TraceFlags trace_flags; + opentelemetry::common::SystemTimestamp now(std::chrono::system_clock::now()); + + // Set all fields of the LogRecord + LogRecord record; + auto resource = opentelemetry::sdk::resource::Resource::Create({{"res1", true}}); + record.SetSeverity(logs_api::Severity::kInvalid); + record.SetBody("Message"); + record.SetResource(resource); + record.SetAttribute("attr1", (int64_t)314159); + record.SetTraceId(trace_id); + record.SetSpanId(span_id); + record.SetTraceFlags(trace_flags); + record.SetTimestamp(now); + + // Test that all fields match what was set + ASSERT_EQ(record.GetSeverity(), logs_api::Severity::kInvalid); + ASSERT_EQ(record.GetBody(), "Message"); + ASSERT_TRUE(nostd::get(record.GetResource().GetAttributes().at("res1"))); + ASSERT_EQ(nostd::get(record.GetAttributes().at("attr1")), 314159); + ASSERT_EQ(record.GetTraceId(), trace_id); + ASSERT_EQ(record.GetSpanId(), span_id); + ASSERT_EQ(record.GetTraceFlags(), trace_flags); + ASSERT_EQ(record.GetTimestamp().time_since_epoch(), now.time_since_epoch()); +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_provider_sdk_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_provider_sdk_test.cc new file mode 100644 index 000000000..3e5e8dfb2 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_provider_sdk_test.cc @@ -0,0 +1,133 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW + +# include +# include "opentelemetry/logs/provider.h" +# include "opentelemetry/nostd/shared_ptr.h" +# include "opentelemetry/nostd/string_view.h" +# include "opentelemetry/sdk/logs/log_record.h" +# include "opentelemetry/sdk/logs/logger.h" +# include "opentelemetry/sdk/logs/logger_provider.h" +# include "opentelemetry/sdk/logs/simple_log_processor.h" + +# include + +using namespace opentelemetry::sdk::logs; +namespace logs_api = opentelemetry::logs; +namespace nostd = opentelemetry::nostd; + +TEST(LoggerProviderSDK, PushToAPI) +{ + auto lp = + nostd::shared_ptr(new opentelemetry::sdk::logs::LoggerProvider()); + logs_api::Provider::SetLoggerProvider(lp); + + // Check that the loggerprovider was correctly pushed into the API + ASSERT_EQ(lp, logs_api::Provider::GetLoggerProvider()); +} + +TEST(LoggerProviderSDK, LoggerProviderGetLoggerSimple) +{ + auto lp = std::shared_ptr(new LoggerProvider()); + + nostd::string_view schema_url{"https://opentelemetry.io/schemas/1.11.0"}; + auto logger1 = lp->GetLogger("logger1", "", "opentelelemtry_library", "", schema_url); + auto logger2 = lp->GetLogger("logger2", "", "", "", schema_url); + + // Check that the logger is not nullptr + ASSERT_NE(logger1, nullptr); + ASSERT_NE(logger2, nullptr); + + auto sdk_logger1 = static_cast(logger1.get()); + auto sdk_logger2 = static_cast(logger2.get()); + ASSERT_EQ(sdk_logger1->GetInstrumentationLibrary().GetName(), "opentelelemtry_library"); + ASSERT_EQ(sdk_logger1->GetInstrumentationLibrary().GetVersion(), ""); + ASSERT_EQ(sdk_logger1->GetInstrumentationLibrary().GetSchemaURL(), schema_url); + + ASSERT_EQ(sdk_logger2->GetInstrumentationLibrary().GetName(), "logger2"); + ASSERT_EQ(sdk_logger2->GetInstrumentationLibrary().GetVersion(), ""); + ASSERT_EQ(sdk_logger2->GetInstrumentationLibrary().GetSchemaURL(), schema_url); + + // Check that two loggers with different names aren't the same instance + ASSERT_NE(logger1, logger2); + + // Check that two loggers with the same name are the same instance + auto logger3 = lp->GetLogger("logger1", "", "opentelelemtry_library", "", schema_url); + ASSERT_EQ(logger1, logger3); + auto sdk_logger3 = static_cast(logger3.get()); + ASSERT_EQ(sdk_logger3->GetInstrumentationLibrary(), sdk_logger1->GetInstrumentationLibrary()); +} + +TEST(LoggerProviderSDK, LoggerProviderLoggerArguments) +{ + // Currently, arguments are not supported by the loggers. + // TODO: Once the logging spec defines what arguments are allowed, add more + // detail to this test + auto lp = std::shared_ptr(new LoggerProvider()); + + nostd::string_view schema_url{"https://opentelemetry.io/schemas/1.11.0"}; + auto logger1 = lp->GetLogger("logger1", "", "opentelelemtry_library", "", schema_url); + + // Check GetLogger(logger_name, args) + std::array sv{"string"}; + nostd::span args{sv}; + auto logger2 = lp->GetLogger("logger2", args, "opentelelemtry_library", "", schema_url); + auto sdk_logger1 = static_cast(logger1.get()); + auto sdk_logger2 = static_cast(logger2.get()); + ASSERT_EQ(sdk_logger2->GetInstrumentationLibrary(), sdk_logger1->GetInstrumentationLibrary()); +} + +class DummyProcessor : public LogProcessor +{ + std::unique_ptr MakeRecordable() noexcept + { + return std::unique_ptr(new LogRecord); + } + + void OnReceive(std::unique_ptr &&record) noexcept {} + bool ForceFlush(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept + { + return true; + } + bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept + { + return true; + } +}; + +TEST(LoggerProviderSDK, GetResource) +{ + // Create a LoggerProvider without a processor + auto resource = opentelemetry::sdk::resource::Resource::Create({{"key", "value"}}); + LoggerProvider lp{nullptr, resource}; + ASSERT_EQ(nostd::get(lp.GetResource().GetAttributes().at("key")), "value"); +} + +TEST(LoggerProviderSDK, Shutdown) +{ + std::unique_ptr processor(new SimpleLogProcessor(nullptr)); + std::vector> processors; + processors.push_back(std::move(processor)); + + LoggerProvider lp(std::make_shared(std::move(processors))); + + EXPECT_TRUE(lp.Shutdown()); + + // It's safe to shutdown again + EXPECT_TRUE(lp.Shutdown()); +} + +TEST(LoggerProviderSDK, ForceFlush) +{ + std::unique_ptr processor(new SimpleLogProcessor(nullptr)); + std::vector> processors; + processors.push_back(std::move(processor)); + + LoggerProvider lp(std::make_shared(std::move(processors))); + + EXPECT_TRUE(lp.ForceFlush()); +} + +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_sdk_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_sdk_test.cc new file mode 100644 index 000000000..3747731f1 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/logger_sdk_test.cc @@ -0,0 +1,97 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/logs/log_record.h" +# include "opentelemetry/sdk/logs/logger.h" + +# include + +using namespace opentelemetry::sdk::logs; +namespace logs_api = opentelemetry::logs; + +TEST(LoggerSDK, LogToNullProcessor) +{ + // Confirm Logger::Log() does not have undefined behavior + // even when there is no processor set + // since it calls Processor::OnReceive() + + auto lp = std::shared_ptr(new LoggerProvider()); + const std::string schema_url{"https://opentelemetry.io/schemas/1.11.0"}; + auto logger = lp->GetLogger("logger", "", "opentelelemtry_library", "", schema_url); + + auto sdk_logger = static_cast(logger.get()); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetName(), "opentelelemtry_library"); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetVersion(), ""); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetSchemaURL(), schema_url); + // Log a sample log record to a nullptr processor + logger->Debug("Test log"); +} + +class MockProcessor final : public LogProcessor +{ +private: + std::shared_ptr record_received_; + +public: + // A processor used for testing that keeps a track of the recordable it received + explicit MockProcessor(std::shared_ptr record_received) noexcept + : record_received_(record_received) + {} + + std::unique_ptr MakeRecordable() noexcept + { + return std::unique_ptr(new LogRecord); + } + // OnReceive stores the record it receives into the shared_ptr recordable passed into its + // constructor + void OnReceive(std::unique_ptr &&record) noexcept + { + // Cast the recordable received into a concrete LogRecord type + auto copy = std::shared_ptr(static_cast(record.release())); + + // Copy over the received log record's severity, name, and body fields over to the recordable + // passed in the constructor + record_received_->SetSeverity(copy->GetSeverity()); + record_received_->SetBody(copy->GetBody()); + } + bool ForceFlush(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept + { + return true; + } + bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept + { + return true; + } +}; + +TEST(LoggerSDK, LogToAProcessor) +{ + // Create an API LoggerProvider and logger + auto api_lp = std::shared_ptr(new LoggerProvider()); + const std::string schema_url{"https://opentelemetry.io/schemas/1.11.0"}; + auto logger = api_lp->GetLogger("logger", "", "opentelelemtry_library", "", schema_url); + + // Cast the API LoggerProvider to an SDK Logger Provider and assert that it is still the same + // LoggerProvider by checking that getting a logger with the same name as the previously defined + // logger is the same instance + auto lp = static_cast(api_lp.get()); + auto logger2 = lp->GetLogger("logger", "", "opentelelemtry_library", "", schema_url); + ASSERT_EQ(logger, logger2); + + auto sdk_logger = static_cast(logger.get()); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetName(), "opentelelemtry_library"); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetVersion(), ""); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetSchemaURL(), schema_url); + // Set a processor for the LoggerProvider + auto shared_recordable = std::shared_ptr(new LogRecord()); + lp->AddProcessor(std::unique_ptr(new MockProcessor(shared_recordable))); + + // Check that the recordable created by the Log() statement is set properly + logger->Log(logs_api::Severity::kWarn, "Log Message"); + + ASSERT_EQ(shared_recordable->GetSeverity(), logs_api::Severity::kWarn); + ASSERT_EQ(shared_recordable->GetBody(), "Log Message"); +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/simple_log_processor_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/simple_log_processor_test.cc new file mode 100644 index 000000000..32c62a508 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/logs/simple_log_processor_test.cc @@ -0,0 +1,152 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/logs/simple_log_processor.h" +# include "opentelemetry/nostd/span.h" +# include "opentelemetry/sdk/logs/exporter.h" +# include "opentelemetry/sdk/logs/log_record.h" + +# include + +# include +# include + +using namespace opentelemetry::sdk::logs; +using namespace opentelemetry::sdk::common; +namespace nostd = opentelemetry::nostd; + +/* + * A test exporter that can return a vector of all the records it has received, + * and keep track of the number of times its Shutdown() function was called. + */ +class TestExporter final : public LogExporter +{ +public: + TestExporter(int *shutdown_counter, + std::shared_ptr>> logs_received, + size_t *batch_size_received) + : shutdown_counter_(shutdown_counter), + logs_received_(logs_received), + batch_size_received(batch_size_received) + {} + + std::unique_ptr MakeRecordable() noexcept override + { + return std::unique_ptr(new LogRecord()); + } + + // Stores the names of the log records this exporter receives to an internal list + ExportResult Export(const nostd::span> &records) noexcept override + { + *batch_size_received = records.size(); + for (auto &record : records) + { + auto log_record = std::unique_ptr(static_cast(record.release())); + + if (log_record != nullptr) + { + logs_received_->push_back(std::move(log_record)); + } + } + return ExportResult::kSuccess; + } + + // Increment the shutdown counter everytime this method is called + bool Shutdown(std::chrono::microseconds timeout) noexcept override + { + *shutdown_counter_ += 1; + return true; + } + +private: + int *shutdown_counter_; + std::shared_ptr>> logs_received_; + size_t *batch_size_received; +}; + +// Tests whether the simple processor successfully creates a batch of size 1 +// and whether the contents of the record is sent to the exporter correctly +TEST(SimpleLogProcessorTest, SendReceivedLogsToExporter) +{ + // Create a simple processor with a TestExporter attached + std::shared_ptr>> logs_received( + new std::vector>); + size_t batch_size_received = 0; + + std::unique_ptr exporter( + new TestExporter(nullptr, logs_received, &batch_size_received)); + + SimpleLogProcessor processor(std::move(exporter)); + + // Send some log records to the processor (which should then send to the TestExporter) + const int num_logs = 5; + for (int i = 0; i < num_logs; i++) + { + auto recordable = processor.MakeRecordable(); + recordable->SetBody("Log Body"); + processor.OnReceive(std::move(recordable)); + + // Verify that the batch of 1 log record sent by processor matches what exporter received + EXPECT_EQ(1, batch_size_received); + } + + // Test whether the processor's log sent matches the log record received by the exporter + EXPECT_EQ(logs_received->size(), num_logs); + for (int i = 0; i < num_logs; i++) + { + EXPECT_EQ("Log Body", logs_received->at(i)->GetBody()); + } +} + +// Tests behavior when calling the processor's ShutDown() multiple times +TEST(SimpleLogProcessorTest, ShutdownCalledOnce) +{ + // Create a TestExporter + int num_shutdowns = 0; + + std::unique_ptr exporter(new TestExporter(&num_shutdowns, nullptr, nullptr)); + + // Create a processor with the previous test exporter + SimpleLogProcessor processor(std::move(exporter)); + + // The first time processor shutdown is called + EXPECT_EQ(0, num_shutdowns); + EXPECT_EQ(true, processor.Shutdown()); + EXPECT_EQ(1, num_shutdowns); + + EXPECT_EQ(true, processor.Shutdown()); + // Processor::ShutDown(), even if called more than once, should only shutdown exporter once + EXPECT_EQ(1, num_shutdowns); +} + +// A test exporter that always returns failure when shut down +class FailShutDownExporter final : public LogExporter +{ +public: + FailShutDownExporter() {} + + std::unique_ptr MakeRecordable() noexcept override + { + return std::unique_ptr(new LogRecord()); + } + + ExportResult Export(const nostd::span> &records) noexcept override + { + return ExportResult::kSuccess; + } + + bool Shutdown(std::chrono::microseconds timeout) noexcept override { return false; } +}; + +// Tests for when when processor should fail to shutdown +TEST(SimpleLogProcessorTest, ShutDownFail) +{ + std::unique_ptr exporter(new FailShutDownExporter()); + SimpleLogProcessor processor(std::move(exporter)); + + // Expect failure result when exporter fails to shutdown + EXPECT_EQ(false, processor.Shutdown()); +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/BUILD new file mode 100644 index 000000000..819a8d225 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/BUILD @@ -0,0 +1,203 @@ +load("//bazel:otel_cc_benchmark.bzl", "otel_cc_benchmark") + +cc_test( + name = "meter_provider_sdk_test", + srcs = [ + "meter_provider_sdk_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/metrics", + "//sdk/src/resource", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "metric_reader_test", + srcs = [ + "metric_reader_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/metrics", + "//sdk/src/resource", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "view_registry_test", + srcs = [ + "view_registry_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/metrics", + "//sdk/src/resource", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "aggregation_test", + srcs = [ + "aggregation_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/metrics", + "//sdk/src/resource", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "sync_metric_storage_test", + srcs = [ + "sync_metric_storage_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/metrics", + "//sdk/src/resource", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "sync_instruments_test", + srcs = [ + "sync_instruments_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/metrics", + "//sdk/src/resource", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "async_metric_storage_test", + srcs = [ + "async_metric_storage_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/metrics", + "//sdk/src/resource", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "observer_result_test", + srcs = [ + "observer_result_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/metrics", + "//sdk/src/resource", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "multi_metric_storage_test", + srcs = [ + "multi_metric_storage_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/metrics", + "//sdk/src/resource", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "attributes_processor_test", + srcs = [ + "attributes_processor_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/metrics", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "attributes_hashmap_test", + srcs = [ + "attributes_hashmap_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/metrics", + "@com_google_googletest//:gtest_main", + ], +) + +otel_cc_benchmark( + name = "attributes_processor_benchmark", + srcs = [ + "attributes_processor_benchmark.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/metrics", + ], +) + +otel_cc_benchmark( + name = "attributes_hashmap_benchmark", + srcs = [ + "attributes_hashmap_benchmark.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//sdk/src/metrics", + ], +) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/CMakeLists.txt new file mode 100644 index 000000000..faf2f2b49 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/CMakeLists.txt @@ -0,0 +1,34 @@ +foreach( + testname + meter_provider_sdk_test + view_registry_test + aggregation_test + attributes_processor_test + attributes_hashmap_test + sync_metric_storage_test + async_metric_storage_test + multi_metric_storage_test + observer_result_test + sync_instruments_test + async_instruments_test + metric_reader_test + periodic_exporting_metric_reader_test) + add_executable(${testname} "${testname}.cc") + target_link_libraries( + ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} + opentelemetry_resources opentelemetry_metrics) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX metrics. + TEST_LIST ${testname}) +endforeach() + +add_executable(attributes_processor_benchmark attributes_processor_benchmark.cc) +target_link_libraries(attributes_processor_benchmark benchmark::benchmark + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_common) + +add_executable(attributes_hashmap_benchmark attributes_hashmap_benchmark.cc) +target_link_libraries(attributes_hashmap_benchmark benchmark::benchmark + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_common) + +add_subdirectory(exemplar) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/aggregation_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/aggregation_test.cc new file mode 100644 index 000000000..f8051776d --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/aggregation_test.cc @@ -0,0 +1,182 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include +# include "opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h" +# include "opentelemetry/sdk/metrics/aggregation/lastvalue_aggregation.h" +# include "opentelemetry/sdk/metrics/aggregation/sum_aggregation.h" + +# include "opentelemetry/nostd/variant.h" + +using namespace opentelemetry::sdk::metrics; +namespace nostd = opentelemetry::nostd; +TEST(Aggregation, LongSumAggregation) +{ + LongSumAggregation aggr; + auto data = aggr.ToPoint(); + ASSERT_TRUE(nostd::holds_alternative(data)); + auto sum_data = nostd::get(data); + ASSERT_TRUE(nostd::holds_alternative(sum_data.value_)); + EXPECT_EQ(nostd::get(sum_data.value_), 0l); + EXPECT_NO_THROW(aggr.Aggregate(12l, {})); + EXPECT_NO_THROW(aggr.Aggregate(0l, {})); + sum_data = nostd::get(aggr.ToPoint()); + EXPECT_EQ(nostd::get(sum_data.value_), 12l); +} + +TEST(Aggregation, DoubleSumAggregation) +{ + DoubleSumAggregation aggr; + auto data = aggr.ToPoint(); + ASSERT_TRUE(nostd::holds_alternative(data)); + auto sum_data = nostd::get(data); + ASSERT_TRUE(nostd::holds_alternative(sum_data.value_)); + EXPECT_EQ(nostd::get(sum_data.value_), 0); + EXPECT_NO_THROW(aggr.Aggregate(12.0, {})); + EXPECT_NO_THROW(aggr.Aggregate(1.0, {})); + sum_data = nostd::get(aggr.ToPoint()); + EXPECT_EQ(nostd::get(sum_data.value_), 13.0); +} + +TEST(Aggregation, LongLastValueAggregation) +{ + LongLastValueAggregation aggr; + auto data = aggr.ToPoint(); + ASSERT_TRUE(nostd::holds_alternative(data)); + auto lastvalue_data = nostd::get(data); + ASSERT_TRUE(nostd::holds_alternative(lastvalue_data.value_)); + EXPECT_EQ(lastvalue_data.is_lastvalue_valid_, false); + EXPECT_NO_THROW(aggr.Aggregate(12l, {})); + EXPECT_NO_THROW(aggr.Aggregate(1l, {})); + lastvalue_data = nostd::get(aggr.ToPoint()); + EXPECT_EQ(nostd::get(lastvalue_data.value_), 1.0); +} + +TEST(Aggregation, DoubleLastValueAggregation) +{ + DoubleLastValueAggregation aggr; + auto data = aggr.ToPoint(); + ASSERT_TRUE(nostd::holds_alternative(data)); + auto lastvalue_data = nostd::get(data); + ASSERT_TRUE(nostd::holds_alternative(lastvalue_data.value_)); + EXPECT_EQ(lastvalue_data.is_lastvalue_valid_, false); + EXPECT_NO_THROW(aggr.Aggregate(12.0, {})); + EXPECT_NO_THROW(aggr.Aggregate(1.0, {})); + lastvalue_data = nostd::get(aggr.ToPoint()); + EXPECT_EQ(nostd::get(lastvalue_data.value_), 1.0); +} + +TEST(Aggregation, LongHistogramAggregation) +{ + LongHistogramAggregation aggr; + auto data = aggr.ToPoint(); + ASSERT_TRUE(nostd::holds_alternative(data)); + auto histogram_data = nostd::get(data); + ASSERT_TRUE(nostd::holds_alternative(histogram_data.sum_)); + ASSERT_TRUE(nostd::holds_alternative>(histogram_data.boundaries_)); + EXPECT_EQ(nostd::get(histogram_data.sum_), 0); + EXPECT_EQ(histogram_data.count_, 0); + EXPECT_NO_THROW(aggr.Aggregate(12l, {})); // lies in fourth bucket + EXPECT_NO_THROW(aggr.Aggregate(100l, {})); // lies in eight bucket + histogram_data = nostd::get(aggr.ToPoint()); + EXPECT_EQ(nostd::get(histogram_data.sum_), 112); + EXPECT_EQ(histogram_data.count_, 2); + EXPECT_EQ(histogram_data.counts_[3], 1); + EXPECT_EQ(histogram_data.counts_[7], 1); + EXPECT_NO_THROW(aggr.Aggregate(13l, {})); // lies in fourth bucket + EXPECT_NO_THROW(aggr.Aggregate(252l, {})); // lies in ninth bucket + histogram_data = nostd::get(aggr.ToPoint()); + EXPECT_EQ(histogram_data.count_, 4); + EXPECT_EQ(histogram_data.counts_[3], 2); + EXPECT_EQ(histogram_data.counts_[8], 1); + + // Merge + LongHistogramAggregation aggr1; + aggr1.Aggregate(1l, {}); + aggr1.Aggregate(11l, {}); + aggr1.Aggregate(26l, {}); + + LongHistogramAggregation aggr2; + aggr2.Aggregate(2l, {}); + aggr2.Aggregate(3l, {}); + aggr2.Aggregate(13l, {}); + aggr2.Aggregate(28l, {}); + aggr2.Aggregate(105l, {}); + + auto aggr3 = aggr1.Merge(aggr2); + histogram_data = nostd::get(aggr3->ToPoint()); + + EXPECT_EQ(histogram_data.count_, 8); // 3 each from aggr1 and aggr2 + EXPECT_EQ(histogram_data.counts_[1], 3); // 1, 2, 3 + EXPECT_EQ(histogram_data.counts_[3], 2); // 11, 13 + EXPECT_EQ(histogram_data.counts_[4], 2); // 25, 28 + EXPECT_EQ(histogram_data.counts_[7], 1); // 105 + + // Diff + auto aggr4 = aggr1.Diff(aggr2); + histogram_data = nostd::get(aggr4->ToPoint()); + EXPECT_EQ(histogram_data.count_, 2); // aggr2:5 - aggr1:3 + EXPECT_EQ(histogram_data.counts_[1], 1); // aggr2(2, 3) - aggr1(1) + EXPECT_EQ(histogram_data.counts_[3], 0); // aggr2(13) - aggr1(11) + EXPECT_EQ(histogram_data.counts_[4], 0); // aggr2(28) - aggr1(25) + EXPECT_EQ(histogram_data.counts_[7], 1); // aggr2(105) - aggr1(0) +} + +TEST(Aggregation, DoubleHistogramAggregation) +{ + DoubleHistogramAggregation aggr; + auto data = aggr.ToPoint(); + ASSERT_TRUE(nostd::holds_alternative(data)); + auto histogram_data = nostd::get(data); + ASSERT_TRUE(nostd::holds_alternative(histogram_data.sum_)); + ASSERT_TRUE(nostd::holds_alternative>(histogram_data.boundaries_)); + EXPECT_EQ(nostd::get(histogram_data.sum_), 0); + EXPECT_EQ(histogram_data.count_, 0); + EXPECT_NO_THROW(aggr.Aggregate(12.0, {})); // lies in fourth bucket + EXPECT_NO_THROW(aggr.Aggregate(100.0, {})); // lies in eight bucket + histogram_data = nostd::get(aggr.ToPoint()); + EXPECT_EQ(nostd::get(histogram_data.sum_), 112); + EXPECT_EQ(histogram_data.count_, 2); + EXPECT_EQ(histogram_data.counts_[3], 1); + EXPECT_EQ(histogram_data.counts_[7], 1); + EXPECT_NO_THROW(aggr.Aggregate(13.0, {})); // lies in fourth bucket + EXPECT_NO_THROW(aggr.Aggregate(252.0, {})); // lies in ninth bucket + histogram_data = nostd::get(aggr.ToPoint()); + EXPECT_EQ(histogram_data.count_, 4); + EXPECT_EQ(histogram_data.counts_[3], 2); + EXPECT_EQ(histogram_data.counts_[8], 1); + EXPECT_EQ(nostd::get(histogram_data.sum_), 377); + + // Merge + DoubleHistogramAggregation aggr1; + aggr1.Aggregate(1.0, {}); + aggr1.Aggregate(11.0, {}); + aggr1.Aggregate(25.1, {}); + + DoubleHistogramAggregation aggr2; + aggr2.Aggregate(2.0, {}); + aggr2.Aggregate(3.0, {}); + aggr2.Aggregate(13.0, {}); + aggr2.Aggregate(28.1, {}); + aggr2.Aggregate(105.0, {}); + + auto aggr3 = aggr1.Merge(aggr2); + histogram_data = nostd::get(aggr3->ToPoint()); + + EXPECT_EQ(histogram_data.count_, 8); // 3 each from aggr1 and aggr2 + EXPECT_EQ(histogram_data.counts_[1], 3); // 1.0, 2.0, 3.0 + EXPECT_EQ(histogram_data.counts_[3], 2); // 11.0, 13.0 + EXPECT_EQ(histogram_data.counts_[4], 2); // 25.1, 28.1 + EXPECT_EQ(histogram_data.counts_[7], 1); // 105.0 + + // Diff + auto aggr4 = aggr1.Diff(aggr2); + histogram_data = nostd::get(aggr4->ToPoint()); + EXPECT_EQ(histogram_data.count_, 2); // aggr2:5 - aggr1:3 + EXPECT_EQ(histogram_data.counts_[1], 1); // aggr2(2.0, 3.0) - aggr1(1.0) + EXPECT_EQ(histogram_data.counts_[3], 0); // aggr2(13.0) - aggr1(11.0) + EXPECT_EQ(histogram_data.counts_[4], 0); // aggr2(28.1) - aggr1(25.1) + EXPECT_EQ(histogram_data.counts_[7], 1); // aggr2(105.0) - aggr1(0) +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/async_instruments_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/async_instruments_test.cc new file mode 100644 index 000000000..ff9504f78 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/async_instruments_test.cc @@ -0,0 +1,60 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/async_instruments.h" + +# include + +using namespace opentelemetry; +using namespace opentelemetry::sdk::metrics; + +using M = std::map; + +void asyc_generate_measurements_long(opentelemetry::metrics::ObserverResult &observer) {} + +void asyc_generate_measurements_double(opentelemetry::metrics::ObserverResult &observer) {} + +TEST(AsyncInstruments, LongObservableCounter) +{ + auto asyc_generate_meas_long = [](opentelemetry::metrics::ObserverResult &observer) {}; + EXPECT_NO_THROW( + LongObservableCounter counter("long_counter", asyc_generate_meas_long, "description", "1")); +} + +TEST(AsyncInstruments, DoubleObservableCounter) +{ + auto asyc_generate_meas_double = [](opentelemetry::metrics::ObserverResult &observer) {}; + EXPECT_NO_THROW(DoubleObservableCounter counter("long_counter", asyc_generate_meas_double, + "description", "1")); +} + +TEST(AsyncInstruments, LongObservableGauge) +{ + auto asyc_generate_meas_long = [](opentelemetry::metrics::ObserverResult &observer) {}; + EXPECT_NO_THROW( + LongObservableGauge counter("long_counter", asyc_generate_meas_long, "description", "1")); +} + +TEST(AsyncInstruments, DoubleObservableGauge) +{ + auto asyc_generate_meas_double = [](opentelemetry::metrics::ObserverResult &observer) {}; + EXPECT_NO_THROW( + DoubleObservableGauge counter("long_counter", asyc_generate_meas_double, "description", "1")); +} + +TEST(AsyncInstruments, LongObservableUpDownCounter) +{ + auto asyc_generate_meas_long = [](opentelemetry::metrics::ObserverResult &observer) {}; + EXPECT_NO_THROW(LongObservableUpDownCounter counter("long_counter", asyc_generate_meas_long, + "description", "1")); +} + +TEST(AsyncInstruments, DoubleObservableUpDownCounter) +{ + auto asyc_generate_meas_double = [](opentelemetry::metrics::ObserverResult &observer) {}; + EXPECT_NO_THROW(DoubleObservableUpDownCounter counter("long_counter", asyc_generate_meas_double, + "description", "1")); +} + +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/async_metric_storage_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/async_metric_storage_test.cc new file mode 100644 index 000000000..2be5332a8 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/async_metric_storage_test.cc @@ -0,0 +1,132 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/state/async_metric_storage.h" +# include "opentelemetry/common/key_value_iterable_view.h" +# include "opentelemetry/sdk/metrics/instruments.h" +# include "opentelemetry/sdk/metrics/meter_context.h" +# include "opentelemetry/sdk/metrics/metric_exporter.h" +# include "opentelemetry/sdk/metrics/metric_reader.h" +# include "opentelemetry/sdk/metrics/observer_result.h" +# include "opentelemetry/sdk/metrics/state/metric_collector.h" + +# include +# include + +using namespace opentelemetry::sdk::metrics; +using namespace opentelemetry::sdk::instrumentationlibrary; +using namespace opentelemetry::sdk::resource; + +using namespace opentelemetry::sdk::metrics; +using namespace opentelemetry::common; +using M = std::map; + +class MockCollectorHandle : public CollectorHandle +{ +public: + MockCollectorHandle(AggregationTemporality temp) : temporality(temp) {} + + AggregationTemporality GetAggregationTemporality() noexcept override { return temporality; } + +private: + AggregationTemporality temporality; +}; + +class WritableMetricStorageTestFixture : public ::testing::TestWithParam +{}; + +class MeasurementFetcher +{ +public: + static void Fetcher(opentelemetry::metrics::ObserverResult &observer_result, + void * /*state*/) + { + fetch_count++; + if (fetch_count == 1) + { + observer_result.Observe(20l, {{"RequestType", "GET"}}); + observer_result.Observe(10l, {{"RequestType", "PUT"}}); + number_of_get += 20l; + number_of_put += 10l; + } + else if (fetch_count == 2) + { + observer_result.Observe(40l, {{"RequestType", "GET"}}); + observer_result.Observe(20l, {{"RequestType", "PUT"}}); + number_of_get += 40l; + number_of_put += 20l; + } + } + + static void init_values() + { + fetch_count = 0; + number_of_get = 0; + number_of_put = 0; + } + + static size_t fetch_count; + static long number_of_get; + static long number_of_put; + static const size_t number_of_attributes = 2; // GET , PUT +}; + +size_t MeasurementFetcher::fetch_count; +long MeasurementFetcher::number_of_get; +long MeasurementFetcher::number_of_put; +const size_t MeasurementFetcher::number_of_attributes; + +TEST_P(WritableMetricStorageTestFixture, TestAggregation) +{ + MeasurementFetcher::init_values(); + AggregationTemporality temporality = GetParam(); + + InstrumentDescriptor instr_desc = {"name", "desc", "1unit", InstrumentType::kObservableCounter, + InstrumentValueType::kLong}; + + auto sdk_start_ts = std::chrono::system_clock::now(); + // Some computation here + auto collection_ts = std::chrono::system_clock::now() + std::chrono::seconds(5); + + std::shared_ptr collector(new MockCollectorHandle(temporality)); + std::vector> collectors; + collectors.push_back(collector); + size_t count_attributes = 0; + long value = 0; + + MeasurementFetcher measurement_fetcher; + opentelemetry::sdk::metrics::AsyncMetricStorage storage(instr_desc, AggregationType::kSum, + MeasurementFetcher::Fetcher, + new DefaultAttributesProcessor()); + + storage.Collect(collector.get(), collectors, sdk_start_ts, collection_ts, + [&](const MetricData data) { + for (auto data_attr : data.point_data_attr_) + { + auto data = opentelemetry::nostd::get(data_attr.point_data); + if (opentelemetry::nostd::get( + data_attr.attributes.find("RequestType")->second) == "GET") + { + EXPECT_EQ(opentelemetry::nostd::get(data.value_), + MeasurementFetcher::number_of_get); + } + else if (opentelemetry::nostd::get( + data_attr.attributes.find("RequestType")->second) == "PUT") + { + EXPECT_EQ(opentelemetry::nostd::get(data.value_), + MeasurementFetcher::number_of_put); + } + count_attributes++; + } + return true; + }); + EXPECT_EQ(MeasurementFetcher::number_of_attributes, count_attributes); +} + +INSTANTIATE_TEST_SUITE_P(WritableMetricStorageTestLong, + WritableMetricStorageTestFixture, + ::testing::Values(AggregationTemporality::kCumulative, + AggregationTemporality::kDelta)); + +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_hashmap_benchmark.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_hashmap_benchmark.cc new file mode 100644 index 000000000..38d515a7e --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_hashmap_benchmark.cc @@ -0,0 +1,53 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/common/attributemap_hash.h" +# include "opentelemetry/sdk/metrics/aggregation/aggregation.h" +# include "opentelemetry/sdk/metrics/aggregation/drop_aggregation.h" +# include "opentelemetry/sdk/metrics/instruments.h" +# include "opentelemetry/sdk/metrics/state/attributes_hashmap.h" + +# include +# include + +using namespace opentelemetry::sdk::metrics; +constexpr size_t MAX_THREADS = 500; +namespace +{ + +void BM_AttributseHashMap(benchmark::State &state) +{ + + AttributesHashMap hash_map; + std::vector workers; + std::vector attributes = {{{"k1", "v1"}, {"k2", "v2"}}, + {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}}}; + + std::function()> create_default_aggregation = + []() -> std::unique_ptr { + return std::unique_ptr(new DropAggregation); + }; + + while (state.KeepRunning()) + { + for (size_t i = 0; i < MAX_THREADS; i++) + { + workers.push_back(std::thread([&]() { + hash_map.GetOrSetDefault(attributes[i % 2], create_default_aggregation)->Aggregate(1l); + benchmark::DoNotOptimize(hash_map.Has(attributes[i % 2])); + })); + } + } + + for (auto &t : workers) + { + t.join(); + } +} + +BENCHMARK(BM_AttributseHashMap); +} // namespace +#endif +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_hashmap_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_hashmap_test.cc new file mode 100644 index 000000000..610744c8e --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_hashmap_test.cc @@ -0,0 +1,71 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/state/attributes_hashmap.h" +# include +# include "opentelemetry/sdk/metrics/aggregation/drop_aggregation.h" +# include "opentelemetry/sdk/metrics/instruments.h" + +# include + +using namespace opentelemetry::sdk::metrics; +namespace nostd = opentelemetry::nostd; + +TEST(AttributesHashMap, BasicTests) +{ + + // Empty map + AttributesHashMap hash_map; + EXPECT_EQ(hash_map.Size(), 0); + MetricAttributes m1 = {{"k1", "v1"}}; + EXPECT_EQ(hash_map.Get(m1), nullptr); + EXPECT_EQ(hash_map.Has(m1), false); + + // Set + std::unique_ptr aggregation1( + new DropAggregation()); // = std::unique_ptr(new DropAggregation); + hash_map.Set(m1, std::move(aggregation1)); + EXPECT_NO_THROW(hash_map.Get(m1)->Aggregate(1l)); + EXPECT_EQ(hash_map.Size(), 1); + EXPECT_EQ(hash_map.Has(m1), true); + + // Set same key again + auto aggregation2 = std::unique_ptr(new DropAggregation()); + hash_map.Set(m1, std::move(aggregation2)); + EXPECT_NO_THROW(hash_map.Get(m1)->Aggregate(1l)); + EXPECT_EQ(hash_map.Size(), 1); + EXPECT_EQ(hash_map.Has(m1), true); + + // Set more enteria + auto aggregation3 = std::unique_ptr(new DropAggregation()); + MetricAttributes m3 = {{"k1", "v1"}, {"k2", "v2"}}; + hash_map.Set(m3, std::move(aggregation3)); + EXPECT_EQ(hash_map.Has(m1), true); + EXPECT_EQ(hash_map.Has(m3), true); + EXPECT_NO_THROW(hash_map.Get(m3)->Aggregate(1l)); + EXPECT_EQ(hash_map.Size(), 2); + + // GetOrSetDefault + std::function()> create_default_aggregation = + []() -> std::unique_ptr { + return std::unique_ptr(new DropAggregation); + }; + MetricAttributes m4 = {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}}; + EXPECT_NO_THROW(hash_map.GetOrSetDefault(m4, create_default_aggregation)->Aggregate(1l)); + EXPECT_EQ(hash_map.Size(), 3); + + // Set attributes with different order - shouldn't create a new entry. + MetricAttributes m5 = {{"k2", "v2"}, {"k1", "v1"}}; + EXPECT_EQ(hash_map.Has(m5), true); + + // GetAllEnteries + size_t count = 0; + hash_map.GetAllEnteries([&count](const MetricAttributes &attributes, Aggregation &aggregation) { + count++; + return true; + }); + EXPECT_EQ(count, hash_map.Size()); +} + +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_processor_benchmark.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_processor_benchmark.cc new file mode 100644 index 000000000..d558a668f --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_processor_benchmark.cc @@ -0,0 +1,27 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include +#ifndef ENABLE_METRICS_PREVIEW +# include +# include "opentelemetry/sdk/metrics/view/attributes_processor.h" +using namespace opentelemetry::sdk::metrics; +namespace +{ +void BM_AttributseProcessorFilter(benchmark::State &state) +{ + std::map attributes = { + {"att1", 10}, {"attr1", 20}, {"attr3", 30}, {"attr4", 40}}; + FilteringAttributesProcessor attributes_processor( + {{"attr2", true}, {"attr4", true}, {"attr6", true}}); + opentelemetry::common::KeyValueIterableView> iterable(attributes); + while (state.KeepRunning()) + { + auto filtered_attributes = attributes_processor.process(iterable); + } +} + +BENCHMARK(BM_AttributseProcessorFilter); +} // namespace +#endif +BENCHMARK_MAIN(); diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_processor_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_processor_test.cc new file mode 100644 index 000000000..d496cc7b0 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/attributes_processor_test.cc @@ -0,0 +1,49 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/view/attributes_processor.h" +# include + +using namespace opentelemetry::sdk::metrics; +using namespace opentelemetry::common; +using namespace opentelemetry::sdk::common; + +TEST(AttributesProcessor, FilteringAttributesProcessor) +{ + const int kNumFilterAttributes = 3; + std::unordered_map filter = { + {"attr2", true}, {"attr4", true}, {"attr6", true}}; + const int kNumAttributes = 6; + std::string keys[kNumAttributes] = {"attr1", "attr2", "attr3", "attr4", "attr5", "attr6"}; + int values[kNumAttributes] = {10, 20, 30, 40, 50, 60}; + std::map attributes = {{keys[0], values[0]}, {keys[1], values[1]}, + {keys[2], values[2]}, {keys[3], values[3]}, + {keys[4], values[4]}, {keys[5], values[5]}}; + FilteringAttributesProcessor attributes_processor(filter); + opentelemetry::common::KeyValueIterableView> iterable(attributes); + auto filtered_attributes = attributes_processor.process(iterable); + for (auto &e : filtered_attributes) + { + EXPECT_FALSE(filter.find(e.first) == filter.end()); + } + EXPECT_EQ(filter.size(), kNumFilterAttributes); +} + +TEST(AttributesProcessor, FilteringAllAttributesProcessor) +{ + const int kNumFilterAttributes = 0; + std::unordered_map filter = {}; + const int kNumAttributes = 6; + std::string keys[kNumAttributes] = {"attr1", "attr2", "attr3", "attr4", "attr5", "attr6"}; + int values[kNumAttributes] = {10, 20, 30, 40, 50, 60}; + std::map attributes = {{keys[0], values[0]}, {keys[1], values[1]}, + {keys[2], values[2]}, {keys[3], values[3]}, + {keys[4], values[4]}, {keys[5], values[5]}}; + FilteringAttributesProcessor attributes_processor(filter); + opentelemetry::common::KeyValueIterableView> iterable(attributes); + auto filtered_attributes = attributes_processor.process(iterable); + EXPECT_EQ(filter.size(), kNumFilterAttributes); +} + +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/BUILD new file mode 100644 index 000000000..6481f679d --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/BUILD @@ -0,0 +1,47 @@ +cc_test( + name = "no_exemplar_reservoir_test", + srcs = [ + "no_exemplar_reservoir_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//api", + "//sdk:headers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "never_sample_filter_test", + srcs = [ + "never_sample_filter_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//api", + "//sdk:headers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "always_sample_filter_test", + srcs = [ + "always_sample_filter_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "//api", + "//sdk:headers", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/CMakeLists.txt new file mode 100644 index 000000000..303294761 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/CMakeLists.txt @@ -0,0 +1,10 @@ +foreach(testname no_exemplar_reservoir_test never_sample_filter_test + always_sample_filter_test) + add_executable(${testname} "${testname}.cc") + target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_metrics) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX metrics. + TEST_LIST ${testname}) +endforeach() diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/always_sample_filter_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/always_sample_filter_test.cc new file mode 100644 index 000000000..cf4e44995 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/always_sample_filter_test.cc @@ -0,0 +1,19 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/exemplar/always_sample_filter.h" +# include + +using namespace opentelemetry::sdk::metrics; + +TEST(AlwaysSampleFilter, SampleMeasurement) +{ + auto filter = opentelemetry::sdk::metrics::AlwaysSampleFilter::GetAlwaysSampleFilter(); + ASSERT_TRUE( + filter->ShouldSampleMeasurement(1.0, MetricAttributes{}, opentelemetry::context::Context{})); + ASSERT_TRUE( + filter->ShouldSampleMeasurement(1l, MetricAttributes{}, opentelemetry::context::Context{})); +} + +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/never_sample_filter_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/never_sample_filter_test.cc new file mode 100644 index 000000000..930c57220 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/never_sample_filter_test.cc @@ -0,0 +1,20 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/context/context.h" +#ifndef ENABLE_METRICS_PREVIEW +# include +# include "opentelemetry/sdk/metrics/exemplar/never_sample_filter.h" + +using namespace opentelemetry::sdk::metrics; + +TEST(NeverSampleFilter, SampleMeasurement) +{ + auto filter = opentelemetry::sdk::metrics::NeverSampleFilter::GetNeverSampleFilter(); + ASSERT_FALSE( + filter->ShouldSampleMeasurement(1.0, MetricAttributes{}, opentelemetry::context::Context{})); + ASSERT_FALSE( + filter->ShouldSampleMeasurement(1l, MetricAttributes{}, opentelemetry::context::Context{})); +} + +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/no_exemplar_reservoir_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/no_exemplar_reservoir_test.cc new file mode 100644 index 000000000..3e16940ff --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/exemplar/no_exemplar_reservoir_test.cc @@ -0,0 +1,22 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h" +# include + +using namespace opentelemetry::sdk::metrics; + +TEST(NoExemplarReservoir, OfferMeasurement) +{ + auto reservoir = opentelemetry::sdk::metrics::NoExemplarReservoir::GetNoExemplarReservoir(); + EXPECT_NO_THROW(reservoir->OfferMeasurement(1.0, MetricAttributes{}, + opentelemetry::context::Context{}, + std::chrono::system_clock::now())); + EXPECT_NO_THROW(reservoir->OfferMeasurement( + 1l, MetricAttributes{}, opentelemetry::context::Context{}, std::chrono::system_clock::now())); + auto exemplar_data = reservoir->CollectAndReset(MetricAttributes{}); + ASSERT_TRUE(exemplar_data.empty()); +} + +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/meter_provider_sdk_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/meter_provider_sdk_test.cc new file mode 100644 index 000000000..b0fabe50b --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/meter_provider_sdk_test.cc @@ -0,0 +1,91 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include +# include "opentelemetry/sdk/metrics/export/metric_producer.h" +# include "opentelemetry/sdk/metrics/meter.h" +# include "opentelemetry/sdk/metrics/meter_provider.h" +# include "opentelemetry/sdk/metrics/metric_exporter.h" +# include "opentelemetry/sdk/metrics/metric_reader.h" +# include "opentelemetry/sdk/metrics/view/instrument_selector.h" +# include "opentelemetry/sdk/metrics/view/meter_selector.h" + +using namespace opentelemetry::sdk::metrics; + +class MockMetricExporter : public MetricExporter +{ + +public: + MockMetricExporter() = default; + opentelemetry::sdk::common::ExportResult Export(const ResourceMetrics &records) noexcept override + { + return opentelemetry::sdk::common::ExportResult::kSuccess; + } + + bool ForceFlush( + std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override + { + return true; + } + + bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept override + { + return true; + } +}; + +class MockMetricReader : public MetricReader +{ +public: + MockMetricReader(std::unique_ptr exporter) : exporter_(std::move(exporter)) {} + virtual bool OnForceFlush(std::chrono::microseconds timeout) noexcept override { return true; } + virtual bool OnShutDown(std::chrono::microseconds timeout) noexcept override { return true; } + virtual void OnInitialized() noexcept override {} + +private: + std::unique_ptr exporter_; +}; + +TEST(MeterProvider, GetMeter) +{ + + MeterProvider mp1; + // std::unique_ptr view{std::unique_ptr()}; + // MeterProvider mp1(std::move(exporters), std::move(readers), std::move(views); + auto m1 = mp1.GetMeter("test"); + auto m2 = mp1.GetMeter("test"); + auto m3 = mp1.GetMeter("different", "1.0.0"); + auto m4 = mp1.GetMeter(""); + auto m5 = mp1.GetMeter(opentelemetry::nostd::string_view{}); + auto m6 = mp1.GetMeter("different", "1.0.0", "https://opentelemetry.io/schemas/1.2.0"); + ASSERT_NE(nullptr, m1); + ASSERT_NE(nullptr, m2); + ASSERT_NE(nullptr, m3); + ASSERT_NE(nullptr, m6); + + // Should return the same instance each time. + ASSERT_EQ(m1, m2); + ASSERT_NE(m1, m3); + ASSERT_EQ(m4, m5); + ASSERT_NE(m3, m6); + + // Should be an sdk::trace::Tracer with the processor attached. +# ifdef OPENTELEMETRY_RTTI_ENABLED + auto sdkMeter1 = dynamic_cast(m1.get()); +# else + auto sdkMeter1 = static_cast(m1.get()); +# endif + ASSERT_NE(nullptr, sdkMeter1); + std::unique_ptr exporter(new MockMetricExporter()); + std::unique_ptr reader{new MockMetricReader(std::move(exporter))}; + ASSERT_NO_THROW(mp1.AddMetricReader(std::move(reader))); + + std::unique_ptr view{std::unique_ptr()}; + std::unique_ptr instrument_selector{ + new InstrumentSelector(InstrumentType::kCounter, "instru1")}; + std::unique_ptr meter_selector{new MeterSelector("name1", "version1", "schema1")}; + ASSERT_NO_THROW( + mp1.AddView(std::move(instrument_selector), std::move(meter_selector), std::move(view))); +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/metric_reader_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/metric_reader_test.cc new file mode 100644 index 000000000..c9c30853d --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/metric_reader_test.cc @@ -0,0 +1,39 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/metric_reader.h" +# include +# include "opentelemetry/sdk/metrics/meter_context.h" +# include "opentelemetry/sdk/metrics/metric_exporter.h" + +using namespace opentelemetry; +using namespace opentelemetry::sdk::instrumentationlibrary; +using namespace opentelemetry::sdk::metrics; + +class MockMetricReader : public MetricReader +{ +public: + MockMetricReader(AggregationTemporality aggr_temporality) : MetricReader(aggr_temporality) {} + + virtual bool OnForceFlush(std::chrono::microseconds timeout) noexcept override { return true; } + virtual bool OnShutDown(std::chrono::microseconds timeout) noexcept override { return true; } + virtual void OnInitialized() noexcept override {} +}; + +TEST(MetricReaderTest, BasicTests) +{ + AggregationTemporality aggr_temporality = AggregationTemporality::kDelta; + std::unique_ptr metric_reader1(new MockMetricReader(aggr_temporality)); + EXPECT_EQ(metric_reader1->GetAggregationTemporality(), aggr_temporality); + + std::shared_ptr meter_context1(new MeterContext()); + EXPECT_NO_THROW(meter_context1->AddMetricReader(std::move(metric_reader1))); + + std::unique_ptr metric_reader2(new MockMetricReader(aggr_temporality)); + std::shared_ptr meter_context2(new MeterContext()); + MetricProducer *metric_producer = + new MetricCollector(std::move(meter_context2), std::move(metric_reader2)); + EXPECT_NO_THROW(metric_producer->Collect([](ResourceMetrics &metric_data) { return true; })); +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/multi_metric_storage_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/multi_metric_storage_test.cc new file mode 100644 index 000000000..d88946485 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/multi_metric_storage_test.cc @@ -0,0 +1,62 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/state/multi_metric_storage.h" +# include "opentelemetry/common/key_value_iterable_view.h" +# include "opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h" +# include "opentelemetry/sdk/metrics/instruments.h" + +# include + +using namespace opentelemetry; +using namespace opentelemetry::sdk::instrumentationlibrary; +using namespace opentelemetry::sdk::metrics; + +class TestMetricStorage : public WritableMetricStorage +{ +public: + void RecordLong(long value, const opentelemetry::context::Context &context) noexcept override + { + num_calls_long++; + } + + void RecordLong(long value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept override + { + num_calls_long++; + } + + void RecordDouble(double value, const opentelemetry::context::Context &context) noexcept override + { + num_calls_double++; + } + + void RecordDouble(double value, + const opentelemetry::common::KeyValueIterable &attributes, + const opentelemetry::context::Context &context) noexcept override + { + num_calls_double++; + } + + size_t num_calls_long; + size_t num_calls_double; +}; + +TEST(MultiMetricStorageTest, BasicTests) +{ + std::shared_ptr storage( + new TestMetricStorage()); + MultiMetricStorage storages{}; + storages.AddStorage(storage); + EXPECT_NO_THROW(storages.RecordLong(10l, opentelemetry::context::Context{})); + EXPECT_NO_THROW(storages.RecordLong(20l, opentelemetry::context::Context{})); + + EXPECT_NO_THROW(storages.RecordDouble(10.0, opentelemetry::context::Context{})); + EXPECT_NO_THROW(storages.RecordLong(30l, opentelemetry::context::Context{})); + + EXPECT_EQ(static_cast(storage.get())->num_calls_long, 3); + EXPECT_EQ(static_cast(storage.get())->num_calls_double, 1); +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/observer_result_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/observer_result_test.cc new file mode 100644 index 000000000..a4cc28ae5 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/observer_result_test.cc @@ -0,0 +1,38 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/observer_result.h" +# include "opentelemetry/sdk/metrics/view/attributes_processor.h" + +# include + +using namespace opentelemetry::sdk::metrics; +TEST(ObserverResult, BasicTests) +{ + const AttributesProcessor *attributes_processor = new DefaultAttributesProcessor(); + + ObserverResult observer_result(attributes_processor); + + observer_result.Observe(10l); + observer_result.Observe(20l); + EXPECT_EQ(observer_result.GetMeasurements().size(), 1); + + std::map m1 = {{"k2", 12}}; + observer_result.Observe( + 30l, opentelemetry::common::KeyValueIterableView>(m1)); + EXPECT_EQ(observer_result.GetMeasurements().size(), 2); + + observer_result.Observe( + 40l, opentelemetry::common::KeyValueIterableView>(m1)); + EXPECT_EQ(observer_result.GetMeasurements().size(), 2); + + std::map m2 = {{"k2", 12}, {"k4", 12}}; + observer_result.Observe( + 40l, opentelemetry::common::KeyValueIterableView>(m2)); + EXPECT_EQ(observer_result.GetMeasurements().size(), 3); + + delete attributes_processor; +} + +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/periodic_exporting_metric_reader_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/periodic_exporting_metric_reader_test.cc new file mode 100644 index 000000000..5219f3110 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/periodic_exporting_metric_reader_test.cc @@ -0,0 +1,81 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW + +# include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.h" +# include "opentelemetry/sdk/metrics/export/metric_producer.h" +# include "opentelemetry/sdk/metrics/metric_exporter.h" + +# include + +using namespace opentelemetry; +using namespace opentelemetry::sdk::instrumentationlibrary; +using namespace opentelemetry::sdk::metrics; + +class MockPushMetricExporter : public MetricExporter +{ +public: + opentelemetry::sdk::common::ExportResult Export(const ResourceMetrics &record) noexcept override + { + records_.push_back(record); + return opentelemetry::sdk::common::ExportResult::kSuccess; + } + + bool ForceFlush( + std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override + { + return false; + } + + bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept override + { + return true; + } + + size_t GetDataCount() { return records_.size(); } + +private: + std::vector records_; +}; + +class MockMetricProducer : public MetricProducer +{ +public: + MockMetricProducer(std::chrono::microseconds sleep_ms = std::chrono::microseconds::zero()) + : sleep_ms_{sleep_ms}, data_sent_size_(0) + {} + + bool Collect(nostd::function_ref callback) noexcept override + { + std::this_thread::sleep_for(sleep_ms_); + data_sent_size_++; + ResourceMetrics data; + callback(data); + return true; + } + + size_t GetDataCount() { return data_sent_size_; } + +private: + std::chrono::microseconds sleep_ms_; + size_t data_sent_size_; +}; + +TEST(PeriodicExporingMetricReader, BasicTests) +{ + std::unique_ptr exporter(new MockPushMetricExporter()); + PeriodicExportingMetricReaderOptions options; + options.export_timeout_millis = std::chrono::milliseconds(200); + options.export_interval_millis = std::chrono::milliseconds(500); + auto exporter_ptr = exporter.get(); + PeriodicExportingMetricReader reader(std::move(exporter), options); + MockMetricProducer producer; + reader.SetMetricProducer(&producer); + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + reader.Shutdown(); + EXPECT_EQ(static_cast(exporter_ptr)->GetDataCount(), + static_cast(&producer)->GetDataCount()); +} + +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/sync_instruments_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/sync_instruments_test.cc new file mode 100644 index 000000000..1a590d8d2 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/sync_instruments_test.cc @@ -0,0 +1,138 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/sync_instruments.h" +# include "opentelemetry/context/context.h" +# include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h" +# include "opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h" +# include "opentelemetry/sdk/metrics/state/multi_metric_storage.h" + +# include +# include +# include + +using namespace opentelemetry; +using namespace opentelemetry::sdk::instrumentationlibrary; +using namespace opentelemetry::sdk::metrics; + +auto instrumentation_library = InstrumentationLibrary::Create("opentelemetry-cpp", "0.1.0"); + +using M = std::map; + +TEST(SyncInstruments, LongCounter) +{ + InstrumentDescriptor instrument_descriptor = { + "long_counter", "description", "1", InstrumentType::kCounter, InstrumentValueType::kLong}; + std::unique_ptr metric_storage(new MultiMetricStorage()); + LongCounter counter(instrument_descriptor, std::move(metric_storage)); + EXPECT_NO_THROW(counter.Add(10l)); + EXPECT_NO_THROW(counter.Add(10l, opentelemetry::context::Context{})); + + EXPECT_NO_THROW(counter.Add( + 10l, opentelemetry::common::KeyValueIterableView({{"abc", "123"}, {"xyz", "456"}}))); + EXPECT_NO_THROW(counter.Add( + 10l, opentelemetry::common::KeyValueIterableView({{"abc", "123"}, {"xyz", "456"}}), + opentelemetry::context::Context{})); + EXPECT_NO_THROW(counter.Add(10l, opentelemetry::common::KeyValueIterableView({}))); + EXPECT_NO_THROW(counter.Add(10l, opentelemetry::common::KeyValueIterableView({}), + opentelemetry::context::Context{})); +} + +TEST(SyncInstruments, DoubleCounter) +{ + InstrumentDescriptor instrument_descriptor = { + "double_counter", "description", "1", InstrumentType::kCounter, InstrumentValueType::kDouble}; + std::unique_ptr metric_storage(new MultiMetricStorage()); + DoubleCounter counter(instrument_descriptor, std::move(metric_storage)); + EXPECT_NO_THROW(counter.Add(10.10)); + EXPECT_NO_THROW(counter.Add(10.10, opentelemetry::context::Context{})); + + EXPECT_NO_THROW(counter.Add( + 10.10, opentelemetry::common::KeyValueIterableView({{"abc", "123"}, {"xyz", "456"}}))); + EXPECT_NO_THROW(counter.Add( + 10.10, opentelemetry::common::KeyValueIterableView({{"abc", "123"}, {"xyz", "456"}}), + opentelemetry::context::Context{})); + EXPECT_NO_THROW(counter.Add(10.10, opentelemetry::common::KeyValueIterableView({}))); + EXPECT_NO_THROW(counter.Add(10.10, opentelemetry::common::KeyValueIterableView({}), + opentelemetry::context::Context{})); +} + +TEST(SyncInstruments, LongUpDownCounter) +{ + InstrumentDescriptor instrument_descriptor = {"long_updowncounter", "description", "1", + InstrumentType::kUpDownCounter, + InstrumentValueType::kLong}; + std::unique_ptr metric_storage(new MultiMetricStorage()); + LongUpDownCounter counter(instrument_descriptor, std::move(metric_storage)); + EXPECT_NO_THROW(counter.Add(10l)); + EXPECT_NO_THROW(counter.Add(10l, opentelemetry::context::Context{})); + + EXPECT_NO_THROW(counter.Add( + 10l, opentelemetry::common::KeyValueIterableView({{"abc", "123"}, {"xyz", "456"}}))); + EXPECT_NO_THROW(counter.Add( + 10l, opentelemetry::common::KeyValueIterableView({{"abc", "123"}, {"xyz", "456"}}), + opentelemetry::context::Context{})); + EXPECT_NO_THROW(counter.Add(10l, opentelemetry::common::KeyValueIterableView({}))); + EXPECT_NO_THROW(counter.Add(10l, opentelemetry::common::KeyValueIterableView({}), + opentelemetry::context::Context{})); +} + +TEST(SyncInstruments, DoubleUpDownCounter) +{ + InstrumentDescriptor instrument_descriptor = {"double_updowncounter", "description", "1", + InstrumentType::kUpDownCounter, + InstrumentValueType::kDouble}; + std::unique_ptr metric_storage(new MultiMetricStorage()); + DoubleUpDownCounter counter(instrument_descriptor, std::move(metric_storage)); + EXPECT_NO_THROW(counter.Add(10.10)); + EXPECT_NO_THROW(counter.Add(10.10, opentelemetry::context::Context{})); + + EXPECT_NO_THROW(counter.Add( + 10.10, opentelemetry::common::KeyValueIterableView({{"abc", "123"}, {"xyz", "456"}}), + opentelemetry::context::Context{})); + EXPECT_NO_THROW(counter.Add( + 10.10, opentelemetry::common::KeyValueIterableView({{"abc", "123"}, {"xyz", "456"}}))); + EXPECT_NO_THROW(counter.Add(10.10, opentelemetry::common::KeyValueIterableView({}), + opentelemetry::context::Context{})); + EXPECT_NO_THROW(counter.Add(10.10, opentelemetry::common::KeyValueIterableView({}))); +} + +TEST(SyncInstruments, LongHistogram) +{ + InstrumentDescriptor instrument_descriptor = { + "long_histogram", "description", "1", InstrumentType::kHistogram, InstrumentValueType::kLong}; + std::unique_ptr metric_storage(new MultiMetricStorage()); + LongHistogram counter(instrument_descriptor, std::move(metric_storage)); + EXPECT_NO_THROW(counter.Record(10l, opentelemetry::context::Context{})); + EXPECT_NO_THROW(counter.Record(-10l, opentelemetry::context::Context{})); // This is ignored + + EXPECT_NO_THROW(counter.Record( + 10l, opentelemetry::common::KeyValueIterableView({{"abc", "123"}, {"xyz", "456"}}), + opentelemetry::context::Context{})); + EXPECT_NO_THROW(counter.Record(10l, opentelemetry::common::KeyValueIterableView({}), + opentelemetry::context::Context{})); +} + +TEST(SyncInstruments, DoubleHistogram) +{ + InstrumentDescriptor instrument_descriptor = {"double_histogram", "description", "1", + InstrumentType::kHistogram, + InstrumentValueType::kDouble}; + std::unique_ptr metric_storage(new MultiMetricStorage()); + DoubleHistogram counter(instrument_descriptor, std::move(metric_storage)); + EXPECT_NO_THROW(counter.Record(10.10, opentelemetry::context::Context{})); + EXPECT_NO_THROW(counter.Record(-10.10, opentelemetry::context::Context{})); // This is ignored. + EXPECT_NO_THROW(counter.Record(std::numeric_limits::quiet_NaN(), + opentelemetry::context::Context{})); // This is ignored too + EXPECT_NO_THROW(counter.Record(std::numeric_limits::infinity(), + opentelemetry::context::Context{})); // This is ignored too + + EXPECT_NO_THROW(counter.Record( + 10.10, opentelemetry::common::KeyValueIterableView({{"abc", "123"}, {"xyz", "456"}}), + opentelemetry::context::Context{})); + EXPECT_NO_THROW(counter.Record(10.10, opentelemetry::common::KeyValueIterableView({}), + opentelemetry::context::Context{})); +} + +#endif \ No newline at end of file diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/sync_metric_storage_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/sync_metric_storage_test.cc new file mode 100644 index 000000000..7dfc4f947 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/sync_metric_storage_test.cc @@ -0,0 +1,246 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/state/sync_metric_storage.h" +# include "opentelemetry/common/key_value_iterable_view.h" +# include "opentelemetry/sdk/metrics/exemplar/no_exemplar_reservoir.h" +# include "opentelemetry/sdk/metrics/instruments.h" +# include "opentelemetry/sdk/metrics/view/attributes_processor.h" + +# include +# include + +using namespace opentelemetry::sdk::metrics; +using namespace opentelemetry::common; +using M = std::map; + +class MockCollectorHandle : public CollectorHandle +{ +public: + MockCollectorHandle(AggregationTemporality temp) : temporality(temp) {} + + AggregationTemporality GetAggregationTemporality() noexcept override { return temporality; } + +private: + AggregationTemporality temporality; +}; + +class WritableMetricStorageTestFixture : public ::testing::TestWithParam +{}; + +TEST_P(WritableMetricStorageTestFixture, LongSumAggregation) +{ + AggregationTemporality temporality = GetParam(); + auto sdk_start_ts = std::chrono::system_clock::now(); + long expected_total_get_requests = 0; + long expected_total_put_requests = 0; + InstrumentDescriptor instr_desc = {"name", "desc", "1unit", InstrumentType::kCounter, + InstrumentValueType::kLong}; + std::map attributes_get = {{"RequestType", "GET"}}; + std::map attributes_put = {{"RequestType", "PUT"}}; + + opentelemetry::sdk::metrics::SyncMetricStorage storage( + instr_desc, AggregationType::kSum, new DefaultAttributesProcessor(), + NoExemplarReservoir::GetNoExemplarReservoir()); + + storage.RecordLong(10l, KeyValueIterableView>(attributes_get), + opentelemetry::context::Context{}); + expected_total_get_requests += 10; + + EXPECT_NO_THROW(storage.RecordLong( + 30l, KeyValueIterableView>(attributes_put), + opentelemetry::context::Context{})); + expected_total_put_requests += 30; + + storage.RecordLong(20l, KeyValueIterableView>(attributes_get), + opentelemetry::context::Context{}); + expected_total_get_requests += 20; + + EXPECT_NO_THROW(storage.RecordLong( + 40l, KeyValueIterableView>(attributes_put), + opentelemetry::context::Context{})); + expected_total_put_requests += 40; + + std::shared_ptr collector(new MockCollectorHandle(temporality)); + std::vector> collectors; + collectors.push_back(collector); + + // Some computation here + auto collection_ts = std::chrono::system_clock::now(); + size_t count_attributes = 0; + storage.Collect( + collector.get(), collectors, sdk_start_ts, collection_ts, [&](const MetricData data) { + for (auto data_attr : data.point_data_attr_) + { + auto data = opentelemetry::nostd::get(data_attr.point_data); + if (opentelemetry::nostd::get( + data_attr.attributes.find("RequestType")->second) == "GET") + { + EXPECT_EQ(opentelemetry::nostd::get(data.value_), expected_total_get_requests); + count_attributes++; + } + else if (opentelemetry::nostd::get( + data_attr.attributes.find("RequestType")->second) == "PUT") + { + EXPECT_EQ(opentelemetry::nostd::get(data.value_), expected_total_put_requests); + count_attributes++; + } + } + return true; + }); + + // In case of delta temporarily, subsequent collection would contain new data points, so resetting + // the counts + if (temporality == AggregationTemporality::kDelta) + { + expected_total_get_requests = 0; + expected_total_put_requests = 0; + } + + EXPECT_NO_THROW(storage.RecordLong( + 50l, KeyValueIterableView>(attributes_get), + opentelemetry::context::Context{})); + expected_total_get_requests += 50; + EXPECT_NO_THROW(storage.RecordLong( + 40l, KeyValueIterableView>(attributes_put), + opentelemetry::context::Context{})); + expected_total_put_requests += 40; + + collection_ts = std::chrono::system_clock::now(); + count_attributes = 0; + storage.Collect( + collector.get(), collectors, sdk_start_ts, collection_ts, [&](const MetricData data) { + for (auto data_attr : data.point_data_attr_) + { + auto data = opentelemetry::nostd::get(data_attr.point_data); + if (opentelemetry::nostd::get( + data_attr.attributes.find("RequestType")->second) == "GET") + { + EXPECT_EQ(opentelemetry::nostd::get(data.value_), expected_total_get_requests); + count_attributes++; + } + else if (opentelemetry::nostd::get( + data_attr.attributes.find("RequestType")->second) == "PUT") + { + EXPECT_EQ(opentelemetry::nostd::get(data.value_), expected_total_put_requests); + count_attributes++; + } + } + return true; + }); +} +INSTANTIATE_TEST_SUITE_P(WritableMetricStorageTestLong, + WritableMetricStorageTestFixture, + ::testing::Values(AggregationTemporality::kCumulative, + AggregationTemporality::kDelta)); + +TEST_P(WritableMetricStorageTestFixture, DoubleSumAggregation) +{ + AggregationTemporality temporality = GetParam(); + auto sdk_start_ts = std::chrono::system_clock::now(); + double expected_total_get_requests = 0; + double expected_total_put_requests = 0; + InstrumentDescriptor instr_desc = {"name", "desc", "1unit", InstrumentType::kCounter, + InstrumentValueType::kDouble}; + std::map attributes_get = {{"RequestType", "GET"}}; + std::map attributes_put = {{"RequestType", "PUT"}}; + + opentelemetry::sdk::metrics::SyncMetricStorage storage( + instr_desc, AggregationType::kSum, new DefaultAttributesProcessor(), + NoExemplarReservoir::GetNoExemplarReservoir()); + + storage.RecordDouble(10.0, + KeyValueIterableView>(attributes_get), + opentelemetry::context::Context{}); + expected_total_get_requests += 10; + + EXPECT_NO_THROW(storage.RecordDouble( + 30.0, KeyValueIterableView>(attributes_put), + opentelemetry::context::Context{})); + expected_total_put_requests += 30; + + storage.RecordDouble(20.0, + KeyValueIterableView>(attributes_get), + opentelemetry::context::Context{}); + expected_total_get_requests += 20; + + EXPECT_NO_THROW(storage.RecordDouble( + 40.0, KeyValueIterableView>(attributes_put), + opentelemetry::context::Context{})); + expected_total_put_requests += 40; + + std::shared_ptr collector(new MockCollectorHandle(temporality)); + std::vector> collectors; + collectors.push_back(collector); + + // Some computation here + auto collection_ts = std::chrono::system_clock::now(); + size_t count_attributes = 0; + storage.Collect( + collector.get(), collectors, sdk_start_ts, collection_ts, [&](const MetricData data) { + for (auto data_attr : data.point_data_attr_) + { + auto data = opentelemetry::nostd::get(data_attr.point_data); + if (opentelemetry::nostd::get( + data_attr.attributes.find("RequestType")->second) == "GET") + { + EXPECT_EQ(opentelemetry::nostd::get(data.value_), expected_total_get_requests); + count_attributes++; + } + else if (opentelemetry::nostd::get( + data_attr.attributes.find("RequestType")->second) == "PUT") + { + EXPECT_EQ(opentelemetry::nostd::get(data.value_), expected_total_put_requests); + count_attributes++; + } + } + return true; + }); + + // In case of delta temporarily, subsequent collection would contain new data points, so resetting + // the counts + if (temporality == AggregationTemporality::kDelta) + { + expected_total_get_requests = 0; + expected_total_put_requests = 0; + } + + EXPECT_NO_THROW(storage.RecordDouble( + 50.0, KeyValueIterableView>(attributes_get), + opentelemetry::context::Context{})); + expected_total_get_requests += 50; + EXPECT_NO_THROW(storage.RecordDouble( + 40.0, KeyValueIterableView>(attributes_put), + opentelemetry::context::Context{})); + expected_total_put_requests += 40; + + collection_ts = std::chrono::system_clock::now(); + count_attributes = 0; + storage.Collect( + collector.get(), collectors, sdk_start_ts, collection_ts, [&](const MetricData data) { + for (auto data_attr : data.point_data_attr_) + { + auto data = opentelemetry::nostd::get(data_attr.point_data); + if (opentelemetry::nostd::get( + data_attr.attributes.find("RequestType")->second) == "GET") + { + EXPECT_EQ(opentelemetry::nostd::get(data.value_), expected_total_get_requests); + count_attributes++; + } + else if (opentelemetry::nostd::get( + data_attr.attributes.find("RequestType")->second) == "PUT") + { + EXPECT_EQ(opentelemetry::nostd::get(data.value_), expected_total_put_requests); + count_attributes++; + } + } + return true; + }); +} +INSTANTIATE_TEST_SUITE_P(WritableMetricStorageTestDouble, + WritableMetricStorageTestFixture, + ::testing::Values(AggregationTemporality::kCumulative, + AggregationTemporality::kDelta)); + +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/view_registry_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/view_registry_test.cc new file mode 100644 index 000000000..8151d3754 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/metrics/view_registry_test.cc @@ -0,0 +1,82 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/view/view_registry.h" +# include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h" +# include "opentelemetry/sdk/metrics/instruments.h" +# include "opentelemetry/sdk/metrics/view/predicate.h" + +# include + +using namespace opentelemetry::sdk::metrics; +using namespace opentelemetry::sdk::instrumentationlibrary; + +TEST(ViewRegistry, FindViewsEmptyRegistry) +{ + InstrumentDescriptor default_instrument_descriptor = { + "test_name", // name + "test_descr", // description + "1", // unit + InstrumentType::kCounter, // instrument type + InstrumentValueType::kLong}; + + auto default_instrumentation_lib = + InstrumentationLibrary::Create("default", "1.0.0", "https://opentelemetry.io/schemas/1.7.0"); + int count = 0; + ViewRegistry registry; + auto status = + registry.FindViews(default_instrument_descriptor, *default_instrumentation_lib.get(), + [&count](const View &view) { + count++; + EXPECT_EQ(view.GetName(), "otel-default-view"); + EXPECT_EQ(view.GetDescription(), ""); + EXPECT_EQ(view.GetAggregationType(), AggregationType::kDefault); + return true; + }); + EXPECT_EQ(count, 1); + EXPECT_EQ(status, true); +} + +TEST(ViewRegistry, FindNonExistingView) +{ + // Add view + const std::string view_name = "test_view"; + const std::string view_description = "test description"; + const std::string instrumentation_name = "name1"; + const std::string instrumentation_version = "version1"; + const std::string instrumentation_schema = "schema1"; + const std::string instrument_name = "testname"; + const InstrumentType instrument_type = InstrumentType::kCounter; + + std::unique_ptr instrument_selector{ + new InstrumentSelector(instrument_type, instrument_name)}; + std::unique_ptr meter_selector{ + new MeterSelector(instrumentation_name, instrumentation_version, instrumentation_schema)}; + std::unique_ptr view = std::unique_ptr(new View(view_name, view_description)); + + ViewRegistry registry; + registry.AddView(std::move(instrument_selector), std::move(meter_selector), std::move(view)); + InstrumentDescriptor default_instrument_descriptor = {instrument_name, // name + "test_descr", // description + "1", // unit + instrument_type, // instrument type + InstrumentValueType::kLong}; + + auto default_instrumentation_lib = InstrumentationLibrary::Create( + instrumentation_name, instrumentation_version, instrumentation_schema); + int count = 0; + auto status = + registry.FindViews(default_instrument_descriptor, *default_instrumentation_lib.get(), + [&count, &view_name, &view_description](const View &view) { + count++; +# if HAVE_WORKING_REGEX + EXPECT_EQ(view.GetName(), view_name); + EXPECT_EQ(view.GetDescription(), view_description); +# endif + return true; + }); + EXPECT_EQ(count, 1); + EXPECT_EQ(status, true); +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/BUILD new file mode 100644 index 000000000..e70f16ee7 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/BUILD @@ -0,0 +1,12 @@ +cc_test( + name = "resource_test", + srcs = [ + "resource_test.cc", + ], + tags = ["test"], + deps = [ + "//api", + "//sdk/src/resource", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/CMakeLists.txt new file mode 100644 index 000000000..514b98680 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/CMakeLists.txt @@ -0,0 +1,9 @@ +foreach(testname resource_test) + add_executable(${testname} "${testname}.cc") + target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_resources) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX resources. + TEST_LIST ${testname}) +endforeach() diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/resource_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/resource_test.cc new file mode 100644 index 000000000..5f058eafb --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/resource/resource_test.cc @@ -0,0 +1,213 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/common/key_value_iterable_view.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/common/attribute_utils.h" +#include "opentelemetry/sdk/resource/experimental_semantic_conventions.h" +#include "opentelemetry/sdk/resource/resource_detector.h" + +#include +#include +#include + +#include + +#if defined(_MSC_VER) +# include "opentelemetry/sdk/common/env_variables.h" +using opentelemetry::sdk::common::setenv; +using opentelemetry::sdk::common::unsetenv; +#endif + +using namespace opentelemetry::sdk::resource; +namespace nostd = opentelemetry::nostd; + +class TestResource : public Resource +{ +public: + TestResource(ResourceAttributes attributes = ResourceAttributes()) : Resource(attributes) {} +}; + +TEST(ResourceTest, create_without_servicename) +{ + ResourceAttributes expected_attributes = { + {"service", "backend"}, + {"version", (uint32_t)1}, + {"cost", 234.23}, + {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkLanguage), "cpp"}, + {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkName), "opentelemetry"}, + {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkVersion), OPENTELEMETRY_SDK_VERSION}, + {OTEL_GET_RESOURCE_ATTR(AttrServiceName), "unknown_service"}}; + + ResourceAttributes attributes = { + {"service", "backend"}, {"version", (uint32_t)1}, {"cost", 234.23}}; + auto resource = Resource::Create(attributes); + auto received_attributes = resource.GetAttributes(); + for (auto &e : received_attributes) + { + EXPECT_TRUE(expected_attributes.find(e.first) != expected_attributes.end()); + if (expected_attributes.find(e.first) != expected_attributes.end()) + { + if (e.first == "version") + EXPECT_EQ(nostd::get(expected_attributes.find(e.first)->second), + nostd::get(e.second)); + else if (e.first == "cost") + EXPECT_EQ(nostd::get(expected_attributes.find(e.first)->second), + nostd::get(e.second)); + else + EXPECT_EQ(opentelemetry::nostd::get(expected_attributes.find(e.first)->second), + opentelemetry::nostd::get(e.second)); + } + } + EXPECT_EQ(received_attributes.size(), expected_attributes.size()); // for missing service.name +} + +TEST(ResourceTest, create_with_servicename) +{ + ResourceAttributes expected_attributes = { + {"version", (uint32_t)1}, + {"cost", 234.23}, + {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkLanguage), "cpp"}, + {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkName), "opentelemetry"}, + {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkVersion), OPENTELEMETRY_SDK_VERSION}, + {OTEL_GET_RESOURCE_ATTR(AttrServiceName), "backend"}, + }; + ResourceAttributes attributes = { + {"service.name", "backend"}, {"version", (uint32_t)1}, {"cost", 234.23}}; + auto resource = Resource::Create(attributes); + auto received_attributes = resource.GetAttributes(); + for (auto &e : received_attributes) + { + EXPECT_TRUE(expected_attributes.find(e.first) != expected_attributes.end()); + if (expected_attributes.find(e.first) != expected_attributes.end()) + { + if (e.first == "version") + EXPECT_EQ(nostd::get(expected_attributes.find(e.first)->second), + nostd::get(e.second)); + else if (e.first == "cost") + EXPECT_EQ(nostd::get(expected_attributes.find(e.first)->second), + nostd::get(e.second)); + else + EXPECT_EQ(nostd::get(expected_attributes.find(e.first)->second), + nostd::get(e.second)); + } + } + EXPECT_EQ(received_attributes.size(), expected_attributes.size()); // for missing service.name +} + +TEST(ResourceTest, create_with_emptyatrributes) +{ + ResourceAttributes expected_attributes = { + {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkLanguage), "cpp"}, + {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkName), "opentelemetry"}, + {OTEL_GET_RESOURCE_ATTR(AttrTelemetrySdkVersion), OPENTELEMETRY_SDK_VERSION}, + {OTEL_GET_RESOURCE_ATTR(AttrServiceName), "unknown_service"}, + }; + ResourceAttributes attributes = {}; + auto resource = Resource::Create(attributes); + auto received_attributes = resource.GetAttributes(); + for (auto &e : received_attributes) + { + EXPECT_TRUE(expected_attributes.find(e.first) != expected_attributes.end()); + if (expected_attributes.find(e.first) != expected_attributes.end()) + { + EXPECT_EQ(opentelemetry::nostd::get(expected_attributes.find(e.first)->second), + opentelemetry::nostd::get(e.second)); + } + } + EXPECT_EQ(received_attributes.size(), expected_attributes.size()); // for missing service.name +} + +TEST(ResourceTest, create_with_schemaurl) +{ + const std::string schema_url = "https://opentelemetry.io/schemas/1.2.0"; + ResourceAttributes attributes = {}; + auto resource = Resource::Create(attributes, schema_url); + auto received_schema_url = resource.GetSchemaURL(); + + EXPECT_EQ(received_schema_url, schema_url); +} + +TEST(ResourceTest, Merge) +{ + TestResource resource1(ResourceAttributes({{"service", "backend"}})); + TestResource resource2(ResourceAttributes({{"host", "service-host"}})); + std::map expected_attributes = {{"service", "backend"}, + {"host", "service-host"}}; + + auto merged_resource = resource1.Merge(resource2); + auto received_attributes = merged_resource.GetAttributes(); + for (auto &e : received_attributes) + { + EXPECT_TRUE(expected_attributes.find(e.first) != expected_attributes.end()); + if (expected_attributes.find(e.first) != expected_attributes.end()) + { + EXPECT_EQ(expected_attributes.find(e.first)->second, nostd::get(e.second)); + } + } + EXPECT_EQ(received_attributes.size(), expected_attributes.size()); +} + +TEST(ResourceTest, MergeEmptyString) +{ + TestResource resource1({{"service", "backend"}, {"host", "service-host"}}); + TestResource resource2({{"service", ""}, {"host", "another-service-host"}}); + std::map expected_attributes = {{"service", ""}, + {"host", "another-service-host"}}; + + auto merged_resource = resource1.Merge(resource2); + auto received_attributes = merged_resource.GetAttributes(); + + for (auto &e : received_attributes) + { + EXPECT_TRUE(expected_attributes.find(e.first) != expected_attributes.end()); + if (expected_attributes.find(e.first) != expected_attributes.end()) + { + EXPECT_EQ(expected_attributes.find(e.first)->second, nostd::get(e.second)); + } + } + EXPECT_EQ(received_attributes.size(), expected_attributes.size()); +} + +#ifndef NO_GETENV +TEST(ResourceTest, OtelResourceDetector) +{ + std::map expected_attributes = {{"k", "v"}}; + + setenv("OTEL_RESOURCE_ATTRIBUTES", "k=v", 1); + + OTELResourceDetector detector; + auto resource = detector.Detect(); + auto received_attributes = resource.GetAttributes(); + for (auto &e : received_attributes) + { + EXPECT_TRUE(expected_attributes.find(e.first) != expected_attributes.end()); + if (expected_attributes.find(e.first) != expected_attributes.end()) + { + EXPECT_EQ(expected_attributes.find(e.first)->second, nostd::get(e.second)); + } + } + EXPECT_EQ(received_attributes.size(), expected_attributes.size()); + + unsetenv("OTEL_RESOURCE_ATTRIBUTES"); +} + +TEST(ResourceTest, OtelResourceDetectorEmptyEnv) +{ + std::map expected_attributes = {}; + unsetenv("OTEL_RESOURCE_ATTRIBUTES"); + OTELResourceDetector detector; + auto resource = detector.Detect(); + auto received_attributes = resource.GetAttributes(); + for (auto &e : received_attributes) + { + EXPECT_TRUE(expected_attributes.find(e.first) != expected_attributes.end()); + if (expected_attributes.find(e.first) != expected_attributes.end()) + { + EXPECT_EQ(expected_attributes.find(e.first)->second, nostd::get(e.second)); + } + } + EXPECT_EQ(received_attributes.size(), expected_attributes.size()); +} +#endif diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/BUILD b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/BUILD new file mode 100644 index 000000000..70e517684 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/BUILD @@ -0,0 +1,155 @@ +load("//bazel:otel_cc_benchmark.bzl", "otel_cc_benchmark") + +cc_test( + name = "tracer_provider_test", + srcs = [ + "tracer_provider_test.cc", + ], + tags = [ + "test", + "trace", + ], + deps = [ + "//sdk/src/resource", + "//sdk/src/trace", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "span_data_test", + srcs = [ + "span_data_test.cc", + ], + tags = [ + "test", + "trace", + ], + deps = [ + "//sdk/src/trace", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "simple_processor_test", + srcs = [ + "simple_processor_test.cc", + ], + tags = [ + "test", + "trace", + ], + deps = [ + "//exporters/memory:in_memory_span_exporter", + "//sdk/src/trace", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "batch_span_processor_test", + srcs = [ + "batch_span_processor_test.cc", + ], + tags = [ + "test", + "trace", + ], + deps = [ + "//sdk/src/trace", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "tracer_test", + srcs = [ + "tracer_test.cc", + ], + tags = [ + "test", + "trace", + ], + deps = [ + "//exporters/memory:in_memory_span_exporter", + "//sdk/src/resource", + "//sdk/src/trace", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "always_on_sampler_test", + srcs = [ + "always_on_sampler_test.cc", + ], + tags = [ + "test", + "trace", + ], + deps = [ + "//sdk/src/trace", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "always_off_sampler_test", + srcs = [ + "always_off_sampler_test.cc", + ], + tags = [ + "test", + "trace", + ], + deps = [ + "//sdk/src/trace", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "parent_sampler_test", + srcs = [ + "parent_sampler_test.cc", + ], + tags = [ + "test", + "trace", + ], + deps = [ + "//sdk/src/trace", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "trace_id_ratio_sampler_test", + srcs = [ + "trace_id_ratio_sampler_test.cc", + ], + tags = [ + "test", + "trace", + ], + deps = [ + "//sdk/src/common:random", + "//sdk/src/trace", + "@com_google_googletest//:gtest_main", + ], +) + +otel_cc_benchmark( + name = "sampler_benchmark", + srcs = ["sampler_benchmark.cc"], + tags = [ + "test", + "trace", + ], + deps = [ + "//exporters/memory:in_memory_span_exporter", + "//sdk/src/resource", + "//sdk/src/trace", + ], +) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/CMakeLists.txt new file mode 100644 index 000000000..b02ff705f --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/CMakeLists.txt @@ -0,0 +1,30 @@ +foreach( + testname + tracer_provider_test + span_data_test + simple_processor_test + tracer_test + always_off_sampler_test + always_on_sampler_test + parent_sampler_test + trace_id_ratio_sampler_test + batch_span_processor_test) + add_executable(${testname} "${testname}.cc") + target_link_libraries( + ${testname} + ${GTEST_BOTH_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + opentelemetry_common + opentelemetry_trace + opentelemetry_resources + opentelemetry_exporter_in_memory) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX trace. + TEST_LIST ${testname}) +endforeach() + +add_executable(sampler_benchmark sampler_benchmark.cc) +target_link_libraries( + sampler_benchmark benchmark::benchmark ${CMAKE_THREAD_LIBS_INIT} + opentelemetry_trace opentelemetry_resources opentelemetry_exporter_in_memory) diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/always_off_sampler_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/always_off_sampler_test.cc new file mode 100644 index 000000000..1c32bd5a8 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/always_off_sampler_test.cc @@ -0,0 +1,42 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include +#include "opentelemetry/sdk/trace/samplers/always_off.h" +#include "opentelemetry/trace/span_context_kv_iterable_view.h" + +using opentelemetry::sdk::trace::AlwaysOffSampler; +using opentelemetry::sdk::trace::Decision; +using opentelemetry::trace::SpanContext; +namespace trace_api = opentelemetry::trace; + +TEST(AlwaysOffSampler, ShouldSample) +{ + AlwaysOffSampler sampler; + + trace_api::TraceId trace_id; + trace_api::SpanKind span_kind = trace_api::SpanKind::kInternal; + + using M = std::map; + M m1 = {{}}; + + using L = std::vector>>; + L l1 = {{SpanContext(false, false), {}}, {SpanContext(false, false), {}}}; + + opentelemetry::common::KeyValueIterableView view{m1}; + trace_api::SpanContextKeyValueIterableView links{l1}; + + auto sampling_result = + sampler.ShouldSample(SpanContext::GetInvalid(), trace_id, "", span_kind, view, links); + + ASSERT_EQ(Decision::DROP, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); + ASSERT_EQ("", sampling_result.trace_state->ToHeader()); +} + +TEST(AlwaysOffSampler, GetDescription) +{ + AlwaysOffSampler sampler; + + ASSERT_EQ("AlwaysOffSampler", sampler.GetDescription()); +} diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/always_on_sampler_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/always_on_sampler_test.cc new file mode 100644 index 000000000..c483f9b8b --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/always_on_sampler_test.cc @@ -0,0 +1,60 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/sdk/trace/samplers/always_on.h" +#include "opentelemetry/trace/span_context_kv_iterable_view.h" + +#include +#include + +using namespace opentelemetry::sdk::trace; +using namespace opentelemetry::nostd; +using opentelemetry::trace::SpanContext; +namespace trace_api = opentelemetry::trace; + +TEST(AlwaysOnSampler, ShouldSample) +{ + AlwaysOnSampler sampler; + + // A buffer of trace_id with no specific meaning + constexpr uint8_t buf[] = {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}; + + trace_api::TraceId trace_id_invalid; + trace_api::TraceId trace_id_valid(buf); + std::map key_value_container = {{"key", 0}}; + + using L = std::vector>>; + L l1 = {{trace_api::SpanContext(false, false), {}}, {trace_api::SpanContext(false, false), {}}}; + + opentelemetry::trace::SpanContextKeyValueIterableView links{l1}; + + // Test with invalid (empty) trace id and empty parent context + auto sampling_result = sampler.ShouldSample( + SpanContext::GetInvalid(), trace_id_invalid, "invalid trace id test", + trace_api::SpanKind::kServer, + opentelemetry::common::KeyValueIterableView>(key_value_container), + links); + + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); + ASSERT_EQ("", sampling_result.trace_state->ToHeader()); + + // Test with a valid trace id and empty parent context + sampling_result = sampler.ShouldSample( + SpanContext::GetInvalid(), trace_id_valid, "valid trace id test", + trace_api::SpanKind::kServer, + opentelemetry::common::KeyValueIterableView>(key_value_container), + links); + + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); + ASSERT_EQ("", sampling_result.trace_state->ToHeader()); +} + +TEST(AlwaysOnSampler, GetDescription) +{ + AlwaysOnSampler sampler; + + ASSERT_EQ("AlwaysOnSampler", sampler.GetDescription()); +} diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/batch_span_processor_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/batch_span_processor_test.cc new file mode 100644 index 000000000..0e6f9c35a --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/batch_span_processor_test.cc @@ -0,0 +1,291 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/trace/batch_span_processor.h" +#include "opentelemetry/sdk/trace/span_data.h" +#include "opentelemetry/sdk/trace/tracer.h" + +#include +#include +#include + +OPENTELEMETRY_BEGIN_NAMESPACE + +/** + * Returns a mock span exporter meant exclusively for testing only + */ +class MockSpanExporter final : public sdk::trace::SpanExporter +{ +public: + MockSpanExporter( + std::shared_ptr>> spans_received, + std::shared_ptr> is_shutdown, + std::shared_ptr> is_export_completed = + std::shared_ptr>(new std::atomic(false)), + const std::chrono::milliseconds export_delay = std::chrono::milliseconds(0)) noexcept + : spans_received_(spans_received), + is_shutdown_(is_shutdown), + is_export_completed_(is_export_completed), + export_delay_(export_delay) + {} + + std::unique_ptr MakeRecordable() noexcept override + { + return std::unique_ptr(new sdk::trace::SpanData); + } + + sdk::common::ExportResult Export( + const nostd::span> &recordables) noexcept override + { + *is_export_completed_ = false; + + std::this_thread::sleep_for(export_delay_); + + for (auto &recordable : recordables) + { + auto span = std::unique_ptr( + static_cast(recordable.release())); + + if (span != nullptr) + { + spans_received_->push_back(std::move(span)); + } + } + + *is_export_completed_ = true; + return sdk::common::ExportResult::kSuccess; + } + + bool Shutdown( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override + { + *is_shutdown_ = true; + return true; + } + + bool IsExportCompleted() { return is_export_completed_->load(); } + +private: + std::shared_ptr>> spans_received_; + std::shared_ptr> is_shutdown_; + std::shared_ptr> is_export_completed_; + // Meant exclusively to test force flush timeout + const std::chrono::milliseconds export_delay_; +}; + +/** + * Fixture Class + */ +class BatchSpanProcessorTestPeer : public testing::Test +{ +public: + std::unique_ptr>> GetTestSpans( + std::shared_ptr processor, + const int num_spans) + { + std::unique_ptr>> test_spans( + new std::vector>); + + for (int i = 0; i < num_spans; ++i) + { + test_spans->push_back(processor->MakeRecordable()); + static_cast(test_spans->at(i).get()) + ->SetName("Span " + std::to_string(i)); + } + + return test_spans; + } +}; + +/* ################################## TESTS ############################################ */ + +TEST_F(BatchSpanProcessorTestPeer, TestShutdown) +{ + std::shared_ptr> is_shutdown(new std::atomic(false)); + std::shared_ptr>> spans_received( + new std::vector>); + + auto batch_processor = + std::shared_ptr(new sdk::trace::BatchSpanProcessor( + std::unique_ptr(new MockSpanExporter(spans_received, is_shutdown)), + sdk::trace::BatchSpanProcessorOptions())); + const int num_spans = 3; + + auto test_spans = GetTestSpans(batch_processor, num_spans); + + for (int i = 0; i < num_spans; ++i) + { + batch_processor->OnEnd(std::move(test_spans->at(i))); + } + + EXPECT_TRUE(batch_processor->Shutdown()); + // It's safe to shutdown again + EXPECT_TRUE(batch_processor->Shutdown()); + + EXPECT_EQ(num_spans, spans_received->size()); + for (int i = 0; i < num_spans; ++i) + { + EXPECT_EQ("Span " + std::to_string(i), spans_received->at(i)->GetName()); + } + + EXPECT_TRUE(is_shutdown->load()); +} + +TEST_F(BatchSpanProcessorTestPeer, TestForceFlush) +{ + std::shared_ptr> is_shutdown(new std::atomic(false)); + std::shared_ptr>> spans_received( + new std::vector>); + + auto batch_processor = + std::shared_ptr(new sdk::trace::BatchSpanProcessor( + std::unique_ptr(new MockSpanExporter(spans_received, is_shutdown)), + sdk::trace::BatchSpanProcessorOptions())); + const int num_spans = 2048; + + auto test_spans = GetTestSpans(batch_processor, num_spans); + + for (int i = 0; i < num_spans; ++i) + { + batch_processor->OnEnd(std::move(test_spans->at(i))); + } + + // Give some time to export + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + EXPECT_TRUE(batch_processor->ForceFlush()); + + EXPECT_EQ(num_spans, spans_received->size()); + for (int i = 0; i < num_spans; ++i) + { + EXPECT_EQ("Span " + std::to_string(i), spans_received->at(i)->GetName()); + } + + // Create some more spans to make sure that the processor still works + auto more_test_spans = GetTestSpans(batch_processor, num_spans); + for (int i = 0; i < num_spans; ++i) + { + batch_processor->OnEnd(std::move(more_test_spans->at(i))); + } + + // Give some time to export the spans + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + EXPECT_TRUE(batch_processor->ForceFlush()); + + EXPECT_EQ(num_spans * 2, spans_received->size()); + for (int i = 0; i < num_spans; ++i) + { + EXPECT_EQ("Span " + std::to_string(i % num_spans), + spans_received->at(num_spans + i)->GetName()); + } +} + +TEST_F(BatchSpanProcessorTestPeer, TestManySpansLoss) +{ + /* Test that when exporting more than max_queue_size spans, some are most likely lost*/ + + std::shared_ptr> is_shutdown(new std::atomic(false)); + std::shared_ptr>> spans_received( + new std::vector>); + + const int max_queue_size = 4096; + + auto batch_processor = + std::shared_ptr(new sdk::trace::BatchSpanProcessor( + std::unique_ptr(new MockSpanExporter(spans_received, is_shutdown)), + sdk::trace::BatchSpanProcessorOptions())); + + auto test_spans = GetTestSpans(batch_processor, max_queue_size); + + for (int i = 0; i < max_queue_size; ++i) + { + batch_processor->OnEnd(std::move(test_spans->at(i))); + } + + // Give some time to export the spans + std::this_thread::sleep_for(std::chrono::milliseconds(700)); + + EXPECT_TRUE(batch_processor->ForceFlush()); + + // Span should be exported by now + EXPECT_GE(max_queue_size, spans_received->size()); +} + +TEST_F(BatchSpanProcessorTestPeer, TestManySpansLossLess) +{ + /* Test that no spans are lost when sending max_queue_size spans */ + + std::shared_ptr> is_shutdown(new std::atomic(false)); + std::shared_ptr>> spans_received( + new std::vector>); + + const int num_spans = 2048; + + auto batch_processor = + std::shared_ptr(new sdk::trace::BatchSpanProcessor( + std::unique_ptr(new MockSpanExporter(spans_received, is_shutdown)), + sdk::trace::BatchSpanProcessorOptions())); + + auto test_spans = GetTestSpans(batch_processor, num_spans); + + for (int i = 0; i < num_spans; ++i) + { + batch_processor->OnEnd(std::move(test_spans->at(i))); + } + + // Give some time to export the spans + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + EXPECT_TRUE(batch_processor->ForceFlush()); + + EXPECT_EQ(num_spans, spans_received->size()); + for (int i = 0; i < num_spans; ++i) + { + EXPECT_EQ("Span " + std::to_string(i), spans_received->at(i)->GetName()); + } +} + +TEST_F(BatchSpanProcessorTestPeer, TestScheduleDelayMillis) +{ + /* Test that max_export_batch_size spans are exported every schedule_delay_millis + seconds */ + + std::shared_ptr> is_shutdown(new std::atomic(false)); + std::shared_ptr> is_export_completed(new std::atomic(false)); + std::shared_ptr>> spans_received( + new std::vector>); + const std::chrono::milliseconds export_delay(0); + const size_t max_export_batch_size = 512; + sdk::trace::BatchSpanProcessorOptions options{}; + options.schedule_delay_millis = std::chrono::milliseconds(2000); + + auto batch_processor = + std::shared_ptr(new sdk::trace::BatchSpanProcessor( + std::unique_ptr( + new MockSpanExporter(spans_received, is_shutdown, is_export_completed, export_delay)), + options)); + + auto test_spans = GetTestSpans(batch_processor, max_export_batch_size); + + for (size_t i = 0; i < max_export_batch_size; ++i) + { + batch_processor->OnEnd(std::move(test_spans->at(i))); + } + + // Sleep for schedule_delay_millis milliseconds + std::this_thread::sleep_for(options.schedule_delay_millis); + + // small delay to give time to export + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + // Spans should be exported by now + EXPECT_TRUE(is_export_completed->load()); + EXPECT_EQ(max_export_batch_size, spans_received->size()); + for (size_t i = 0; i < max_export_batch_size; ++i) + { + EXPECT_EQ("Span " + std::to_string(i), spans_received->at(i)->GetName()); + } +} + +OPENTELEMETRY_END_NAMESPACE diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/parent_sampler_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/parent_sampler_test.cc new file mode 100644 index 000000000..124287598 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/parent_sampler_test.cc @@ -0,0 +1,73 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include "opentelemetry/sdk/trace/samplers/always_off.h" +#include "opentelemetry/sdk/trace/samplers/always_on.h" +#include "opentelemetry/sdk/trace/samplers/parent.h" +#include "opentelemetry/trace/span_context_kv_iterable_view.h" + +using opentelemetry::sdk::trace::AlwaysOffSampler; +using opentelemetry::sdk::trace::AlwaysOnSampler; +using opentelemetry::sdk::trace::Decision; +using opentelemetry::sdk::trace::ParentBasedSampler; +namespace trace_api = opentelemetry::trace; + +TEST(ParentBasedSampler, ShouldSample) +{ + ParentBasedSampler sampler_off(std::make_shared()); + ParentBasedSampler sampler_on(std::make_shared()); + + // Set up parameters + uint8_t trace_id_buffer[trace_api::TraceId::kSize] = {1}; + trace_api::TraceId trace_id{trace_id_buffer}; + uint8_t span_id_buffer[trace_api::SpanId::kSize] = {1}; + trace_api::SpanId span_id{span_id_buffer}; + + trace_api::SpanKind span_kind = trace_api::SpanKind::kInternal; + using M = std::map; + M m1 = {{}}; + + using L = std::vector>>; + L l1 = {{trace_api::SpanContext(false, false), {}}, {trace_api::SpanContext(false, false), {}}}; + + opentelemetry::common::KeyValueIterableView view{m1}; + trace_api::SpanContextKeyValueIterableView links{l1}; + auto trace_state = trace_api::TraceState::FromHeader("congo=t61rcWkgMzE"); + trace_api::SpanContext parent_context_sampled(trace_id, span_id, trace_api::TraceFlags{1}, false, + trace_state); + trace_api::SpanContext parent_context_nonsampled(trace_id, span_id, trace_api::TraceFlags{0}, + false, trace_state); + + // Case 1: Parent doesn't exist. Return result of delegateSampler() + auto sampling_result = sampler_off.ShouldSample(trace_api::SpanContext::GetInvalid(), trace_id, + "", span_kind, view, links); + auto sampling_result2 = sampler_on.ShouldSample(trace_api::SpanContext::GetInvalid(), trace_id, + "", span_kind, view, links); + + ASSERT_EQ(Decision::DROP, sampling_result.decision); + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result2.decision); + ASSERT_EQ("", sampling_result.trace_state->ToHeader()); + ASSERT_EQ("", sampling_result2.trace_state->ToHeader()); + + // Case 2: Parent exists and SampledFlag is true + auto sampling_result3 = + sampler_off.ShouldSample(parent_context_sampled, trace_id, "", span_kind, view, links); + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result3.decision); + ASSERT_EQ("congo=t61rcWkgMzE", sampling_result3.trace_state->ToHeader()); + + // Case 3: Parent exists and SampledFlag is false + auto sampling_result4 = + sampler_on.ShouldSample(parent_context_nonsampled, trace_id, "", span_kind, view, links); + ASSERT_EQ(Decision::DROP, sampling_result4.decision); + ASSERT_EQ("congo=t61rcWkgMzE", sampling_result4.trace_state->ToHeader()); +} + +TEST(ParentBasedSampler, GetDescription) +{ + ParentBasedSampler sampler(std::make_shared()); + ASSERT_EQ("ParentBased{AlwaysOffSampler}", sampler.GetDescription()); + ParentBasedSampler sampler2(std::make_shared()); + ASSERT_EQ("ParentBased{AlwaysOnSampler}", sampler2.GetDescription()); +} diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/sampler_benchmark.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/sampler_benchmark.cc new file mode 100644 index 000000000..b09cb5b0c --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/sampler_benchmark.cc @@ -0,0 +1,156 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/exporters/memory/in_memory_span_exporter.h" +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/sdk/trace/sampler.h" +#include "opentelemetry/sdk/trace/samplers/always_off.h" +#include "opentelemetry/sdk/trace/samplers/always_on.h" +#include "opentelemetry/sdk/trace/samplers/parent.h" +#include "opentelemetry/sdk/trace/samplers/trace_id_ratio.h" +#include "opentelemetry/sdk/trace/simple_processor.h" +#include "opentelemetry/sdk/trace/span_data.h" +#include "opentelemetry/sdk/trace/tracer.h" + +#include + +#include + +using namespace opentelemetry::sdk::trace; +using opentelemetry::exporter::memory::InMemorySpanExporter; +using opentelemetry::trace::SpanContext; + +namespace +{ +// Sampler constructor used as a baseline to compare with other samplers +void BM_AlwaysOffSamplerConstruction(benchmark::State &state) +{ + while (state.KeepRunning()) + { + benchmark::DoNotOptimize(AlwaysOffSampler()); + } +} +BENCHMARK(BM_AlwaysOffSamplerConstruction); + +// Sampler constructor used as a baseline to compare with other samplers +void BM_AlwaysOnSamplerConstruction(benchmark::State &state) +{ + while (state.KeepRunning()) + { + benchmark::DoNotOptimize(AlwaysOnSampler()); + } +} +BENCHMARK(BM_AlwaysOnSamplerConstruction); + +void BM_ParentBasedSamplerConstruction(benchmark::State &state) +{ + while (state.KeepRunning()) + { + benchmark::DoNotOptimize(ParentBasedSampler(std::make_shared())); + } +} +BENCHMARK(BM_ParentBasedSamplerConstruction); + +void BM_TraceIdRatioBasedSamplerConstruction(benchmark::State &state) +{ + while (state.KeepRunning()) + { + benchmark::DoNotOptimize(TraceIdRatioBasedSampler(0.01)); + } +} +BENCHMARK(BM_TraceIdRatioBasedSamplerConstruction); + +// Sampler Helper Function +void BenchmarkShouldSampler(Sampler &sampler, benchmark::State &state) +{ + opentelemetry::trace::TraceId trace_id; + opentelemetry::trace::SpanKind span_kind = opentelemetry::trace::SpanKind::kInternal; + + using M = std::map; + M m1 = {{}}; + + using L = std::vector>>; + L l1 = {{trace_api::SpanContext(false, false), {}}, {trace_api::SpanContext(false, false), {}}}; + + opentelemetry::common::KeyValueIterableView view{m1}; + trace_api::SpanContextKeyValueIterableView links{l1}; + + while (state.KeepRunning()) + { + auto invalid_ctx = SpanContext::GetInvalid(); + benchmark::DoNotOptimize( + sampler.ShouldSample(invalid_ctx, trace_id, "", span_kind, view, links)); + } +} + +// Sampler used as a baseline to compare with other samplers +void BM_AlwaysOffSamplerShouldSample(benchmark::State &state) +{ + AlwaysOffSampler sampler; + + BenchmarkShouldSampler(sampler, state); +} +BENCHMARK(BM_AlwaysOffSamplerShouldSample); + +// Sampler used as a baseline to compare with other samplers +void BM_AlwaysOnSamplerShouldSample(benchmark::State &state) +{ + AlwaysOnSampler sampler; + + BenchmarkShouldSampler(sampler, state); +} +BENCHMARK(BM_AlwaysOnSamplerShouldSample); + +void BM_ParentBasedSamplerShouldSample(benchmark::State &state) +{ + ParentBasedSampler sampler(std::make_shared()); + + BenchmarkShouldSampler(sampler, state); +} +BENCHMARK(BM_ParentBasedSamplerShouldSample); + +void BM_TraceIdRatioBasedSamplerShouldSample(benchmark::State &state) +{ + TraceIdRatioBasedSampler sampler(0.01); + + BenchmarkShouldSampler(sampler, state); +} +BENCHMARK(BM_TraceIdRatioBasedSamplerShouldSample); + +// Sampler Helper Function +void BenchmarkSpanCreation(std::shared_ptr sampler, benchmark::State &state) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::unique_ptr processor(new SimpleSpanProcessor(std::move(exporter))); + std::vector> processors; + processors.push_back(std::move(processor)); + auto context = std::make_shared(std::move(processors)); + auto resource = opentelemetry::sdk::resource::Resource::Create({}); + auto tracer = std::shared_ptr(new Tracer(context)); + + while (state.KeepRunning()) + { + auto span = tracer->StartSpan("span"); + + span->SetAttribute("attr1", 3.1); + + span->End(); + } +} + +// Test to measure performance for span creation +void BM_SpanCreation(benchmark::State &state) +{ + BenchmarkSpanCreation(std::move(std::make_shared()), state); +} +BENCHMARK(BM_SpanCreation); + +// Test to measure performance overhead for no-op span creation +void BM_NoopSpanCreation(benchmark::State &state) +{ + BenchmarkSpanCreation(std::move(std::make_shared()), state); +} +BENCHMARK(BM_NoopSpanCreation); + +} // namespace +BENCHMARK_MAIN(); diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/simple_processor_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/simple_processor_test.cc new file mode 100644 index 000000000..9398b922a --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/simple_processor_test.cc @@ -0,0 +1,75 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/trace/simple_processor.h" +#include "opentelemetry/exporters/memory/in_memory_span_exporter.h" +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/sdk/trace/exporter.h" +#include "opentelemetry/sdk/trace/span_data.h" + +#include + +using namespace opentelemetry::sdk::trace; +using namespace opentelemetry::sdk::common; +using opentelemetry::exporter::memory::InMemorySpanData; +using opentelemetry::exporter::memory::InMemorySpanExporter; +using opentelemetry::trace::SpanContext; + +TEST(SimpleProcessor, ToInMemorySpanExporter) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + SimpleSpanProcessor processor(std::move(exporter)); + + auto recordable = processor.MakeRecordable(); + + processor.OnStart(*recordable, SpanContext::GetInvalid()); + + ASSERT_EQ(0, span_data->GetSpans().size()); + + processor.OnEnd(std::move(recordable)); + + ASSERT_EQ(1, span_data->GetSpans().size()); + + EXPECT_TRUE(processor.Shutdown()); +} + +// An exporter that does nothing but record (and give back ) the # of times Shutdown was called. +class RecordShutdownExporter final : public SpanExporter +{ +public: + RecordShutdownExporter(int *shutdown_counter) : shutdown_counter_(shutdown_counter) {} + + std::unique_ptr MakeRecordable() noexcept override + { + return std::unique_ptr(new SpanData()); + } + + ExportResult Export( + const opentelemetry::nostd::span> &recordables) noexcept override + { + return ExportResult::kSuccess; + } + + bool Shutdown( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override + { + *shutdown_counter_ += 1; + return true; + } + +private: + int *shutdown_counter_; +}; + +TEST(SimpleSpanProcessor, ShutdownCalledOnce) +{ + int shutdowns = 0; + std::unique_ptr exporter(new RecordShutdownExporter(&shutdowns)); + SimpleSpanProcessor processor(std::move(exporter)); + EXPECT_EQ(0, shutdowns); + processor.Shutdown(); + EXPECT_EQ(1, shutdowns); + processor.Shutdown(); + EXPECT_EQ(1, shutdowns); +} diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/span_data_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/span_data_test.cc new file mode 100644 index 000000000..7a6c66c91 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/span_data_test.cc @@ -0,0 +1,135 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/trace/span_data.h" +#include "opentelemetry/nostd/variant.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/span_id.h" +#include "opentelemetry/trace/trace_id.h" + +#include + +using opentelemetry::sdk::trace::SpanData; +namespace trace_api = opentelemetry::trace; +namespace common = opentelemetry::common; +namespace nostd = opentelemetry::nostd; + +TEST(SpanData, DefaultValues) +{ + trace_api::SpanContext empty_span_context{false, false}; + trace_api::SpanId zero_span_id; + SpanData data; + + ASSERT_EQ(data.GetTraceId(), empty_span_context.trace_id()); + ASSERT_EQ(data.GetSpanId(), empty_span_context.span_id()); + ASSERT_EQ(data.GetSpanContext(), empty_span_context); + ASSERT_EQ(data.GetParentSpanId(), zero_span_id); + ASSERT_EQ(data.GetName(), ""); + ASSERT_EQ(data.GetStatus(), trace_api::StatusCode::kUnset); + ASSERT_EQ(data.GetDescription(), ""); + ASSERT_EQ(data.GetStartTime().time_since_epoch(), std::chrono::nanoseconds(0)); + ASSERT_EQ(data.GetDuration(), std::chrono::nanoseconds(0)); + ASSERT_EQ(data.GetAttributes().size(), 0); + ASSERT_EQ(data.GetEvents().size(), 0); +} + +TEST(SpanData, Set) +{ + constexpr uint8_t trace_id_buf[] = {1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8}; + constexpr uint8_t span_id_buf[] = {1, 2, 3, 4, 5, 6, 7, 8}; + constexpr uint8_t parent_span_id_buf[] = {8, 7, 6, 5, 4, 3, 2, 1}; + trace_api::TraceId trace_id{trace_id_buf}; + trace_api::SpanId span_id{span_id_buf}; + trace_api::SpanId parent_span_id{parent_span_id_buf}; + const auto trace_state = trace_api::TraceState::GetDefault()->Set("key1", "value"); + const trace_api::SpanContext span_context{ + trace_id, span_id, trace_api::TraceFlags{trace_api::TraceFlags::kIsSampled}, true, + trace_state}; + common::SystemTimestamp now(std::chrono::system_clock::now()); + + SpanData data; + data.SetIdentity(span_context, parent_span_id); + data.SetName("span name"); + data.SetSpanKind(trace_api::SpanKind::kServer); + data.SetStatus(trace_api::StatusCode::kOk, "description"); + data.SetStartTime(now); + data.SetDuration(std::chrono::nanoseconds(1000000)); + data.SetAttribute("attr1", (int64_t)314159); + data.AddEvent("event1", now); + + ASSERT_EQ(data.GetTraceId(), trace_id); + ASSERT_EQ(data.GetSpanId(), span_id); + ASSERT_EQ(data.GetSpanContext(), span_context); + std::string trace_state_key1_value; + ASSERT_EQ(data.GetSpanContext().trace_state()->Get("key1", trace_state_key1_value), true); + ASSERT_EQ(trace_state_key1_value, "value"); + ASSERT_EQ(data.GetParentSpanId(), parent_span_id); + ASSERT_EQ(data.GetName(), "span name"); + ASSERT_EQ(data.GetSpanKind(), trace_api::SpanKind::kServer); + ASSERT_EQ(data.GetStatus(), trace_api::StatusCode::kOk); + ASSERT_EQ(data.GetDescription(), "description"); + ASSERT_EQ(data.GetStartTime().time_since_epoch(), now.time_since_epoch()); + ASSERT_EQ(data.GetDuration(), std::chrono::nanoseconds(1000000)); + ASSERT_EQ(nostd::get(data.GetAttributes().at("attr1")), 314159); + ASSERT_EQ(data.GetEvents().at(0).GetName(), "event1"); + ASSERT_EQ(data.GetEvents().at(0).GetTimestamp(), now); +} + +TEST(SpanData, EventAttributes) +{ + SpanData data; + const int kNumAttributes = 3; + std::string keys[kNumAttributes] = {"attr1", "attr2", "attr3"}; + int64_t values[kNumAttributes] = {3, 5, 20}; + std::map attributes = { + {keys[0], values[0]}, {keys[1], values[1]}, {keys[2], values[2]}}; + + data.AddEvent("Test Event", std::chrono::system_clock::now(), + common::KeyValueIterableView>(attributes)); + + for (int i = 0; i < kNumAttributes; i++) + { + EXPECT_EQ(nostd::get(data.GetEvents().at(0).GetAttributes().at(keys[i])), values[i]); + } +} + +TEST(SpanData, Resources) +{ + SpanData data; + auto resource = opentelemetry::sdk::resource::Resource::Create({}); + auto input_attr = resource.GetAttributes(); + data.SetResource(resource); + auto output_attr = data.GetResource().GetAttributes(); + EXPECT_EQ(input_attr, output_attr); +} + +TEST(SpanData, Links) +{ + SpanData data; + const int kNumAttributes = 3; + std::string keys[kNumAttributes] = {"attr1", "attr2", "attr3"}; + int64_t values[kNumAttributes] = {4, 12, 33}; + std::map attributes = { + {keys[0], values[0]}, {keys[1], values[1]}, {keys[2], values[2]}}; + + // produce valid SpanContext with pseudo span and trace Id. + uint8_t span_id_buf[trace_api::SpanId::kSize] = { + 1, + }; + trace_api::SpanId span_id{span_id_buf}; + uint8_t trace_id_buf[trace_api::TraceId::kSize] = { + 2, + }; + trace_api::TraceId trace_id{trace_id_buf}; + const auto span_context = trace_api::SpanContext( + trace_id, span_id, trace_api::TraceFlags{trace_api::TraceFlags::kIsSampled}, true); + + data.AddLink(span_context, + common::KeyValueIterableView>(attributes)); + + EXPECT_EQ(data.GetLinks().at(0).GetSpanContext(), span_context); + for (int i = 0; i < kNumAttributes; i++) + { + EXPECT_EQ(nostd::get(data.GetLinks().at(0).GetAttributes().at(keys[i])), values[i]); + } +} diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/trace_id_ratio_sampler_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/trace_id_ratio_sampler_test.cc new file mode 100644 index 000000000..1b0088f13 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/trace_id_ratio_sampler_test.cc @@ -0,0 +1,258 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/trace/samplers/trace_id_ratio.h" +#include "opentelemetry/trace/span_context_kv_iterable_view.h" +#include "src/common/random.h" + +#include +#include +#include + +using opentelemetry::sdk::common::Random; +using opentelemetry::sdk::trace::Decision; +using opentelemetry::sdk::trace::TraceIdRatioBasedSampler; +namespace trace_api = opentelemetry::trace; +namespace common = opentelemetry::common; + +namespace +{ +/* + * Helper function for running TraceIdBased sampler tests. + * Given a span context, sampler, and number of iterations this function + * will return the number of RECORD_AND_SAMPLE decision based on randomly + * generated traces. + * + * @param context a required valid span context + * @param sampler a required valid sampler + * @param iterations a required number specifying the number of times to + * generate a random trace_id and check if it should sample using the provided + * provider and context + */ +int RunShouldSampleCountDecision(trace_api::SpanContext &context, + TraceIdRatioBasedSampler &sampler, + int iterations) +{ + int actual_count = 0; + + trace_api::SpanKind span_kind = trace_api::SpanKind::kInternal; + + using M = std::map; + M m1 = {{}}; + + using L = std::vector>>; + L l1 = {{trace_api::SpanContext(false, false), {}}, {trace_api::SpanContext(false, false), {}}}; + + common::KeyValueIterableView view{m1}; + trace_api::SpanContextKeyValueIterableView links{l1}; + + for (int i = 0; i < iterations; ++i) + { + uint8_t buf[16] = {0}; + Random::GenerateRandomBuffer(buf); + + trace_api::TraceId trace_id(buf); + + auto result = sampler.ShouldSample(context, trace_id, "", span_kind, view, links); + if (result.decision == Decision::RECORD_AND_SAMPLE) + { + ++actual_count; + } + } + + return actual_count; +} +} // namespace + +TEST(TraceIdRatioBasedSampler, ShouldSampleWithoutContext) +{ + trace_api::TraceId invalid_trace_id; + + trace_api::SpanKind span_kind = trace_api::SpanKind::kInternal; + + using M = std::map; + M m1 = {{}}; + + using L = std::vector>>; + L l1 = {{trace_api::SpanContext(false, false), {}}, {trace_api::SpanContext(false, false), {}}}; + + common::KeyValueIterableView view{m1}; + trace_api::SpanContextKeyValueIterableView links{l1}; + + TraceIdRatioBasedSampler s1(0.01); + + auto sampling_result = s1.ShouldSample(trace_api::SpanContext::GetInvalid(), invalid_trace_id, "", + span_kind, view, links); + + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); + + constexpr uint8_t buf[] = {0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0, 0, 0, 0, 0, 0, 0}; + trace_api::TraceId valid_trace_id(buf); + + sampling_result = s1.ShouldSample(trace_api::SpanContext::GetInvalid(), valid_trace_id, "", + span_kind, view, links); + + ASSERT_EQ(Decision::DROP, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); + + TraceIdRatioBasedSampler s2(0.50000001); + + sampling_result = s2.ShouldSample(trace_api::SpanContext::GetInvalid(), valid_trace_id, "", + span_kind, view, links); + + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); + + TraceIdRatioBasedSampler s3(0.49999999); + + sampling_result = s3.ShouldSample(trace_api::SpanContext::GetInvalid(), valid_trace_id, "", + span_kind, view, links); + + ASSERT_EQ(Decision::DROP, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); + + TraceIdRatioBasedSampler s4(0.50000000); + + sampling_result = s4.ShouldSample(trace_api::SpanContext::GetInvalid(), valid_trace_id, "", + span_kind, view, links); + + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); +} + +TEST(TraceIdRatioBasedSampler, ShouldSampleWithContext) +{ + + uint8_t trace_id_buffer[trace_api::TraceId::kSize] = {1}; + trace_api::TraceId trace_id{trace_id_buffer}; + uint8_t span_id_buffer[trace_api::SpanId::kSize] = {1}; + trace_api::SpanId span_id{span_id_buffer}; + + trace_api::SpanKind span_kind = trace_api::SpanKind::kInternal; + trace_api::SpanContext c1(trace_id, span_id, trace_api::TraceFlags{0}, false); + trace_api::SpanContext c2(trace_id, span_id, trace_api::TraceFlags{1}, false); + trace_api::SpanContext c3(trace_id, span_id, trace_api::TraceFlags{0}, true); + trace_api::SpanContext c4(trace_id, span_id, trace_api::TraceFlags{1}, true); + + using M = std::map; + M m1 = {{}}; + + using L = std::vector>>; + L l1 = {{trace_api::SpanContext(false, false), {}}, {trace_api::SpanContext(false, false), {}}}; + + common::KeyValueIterableView view{m1}; + trace_api::SpanContextKeyValueIterableView links{l1}; + + TraceIdRatioBasedSampler s1(0.01); + + auto sampling_result = s1.ShouldSample(c1, trace_id, "", span_kind, view, links); + + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); + + sampling_result = s1.ShouldSample(c2, trace_id, "", span_kind, view, links); + + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); + + sampling_result = s1.ShouldSample(c3, trace_id, "", span_kind, view, links); + + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); + + sampling_result = s1.ShouldSample(c4, trace_id, "", span_kind, view, links); + + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); +} + +TEST(TraceIdRatioBasedSampler, TraceIdRatioBasedSamplerHalf) +{ + double ratio = 0.5; + int iterations = 100000; + int expected_count = static_cast(iterations * ratio); + int variance = static_cast(iterations * 0.01); + + trace_api::SpanContext c(true, true); + TraceIdRatioBasedSampler s(ratio); + + int actual_count = RunShouldSampleCountDecision(c, s, iterations); + + ASSERT_TRUE(actual_count < (expected_count + variance)); + ASSERT_TRUE(actual_count > (expected_count - variance)); +} + +TEST(TraceIdRatioBasedSampler, TraceIdRatioBasedSamplerOnePercent) +{ + double ratio = 0.01; + int iterations = 100000; + int expected_count = static_cast(iterations * ratio); + int variance = static_cast(iterations * 0.01); + + trace_api::SpanContext c(true, true); + TraceIdRatioBasedSampler s(ratio); + + int actual_count = RunShouldSampleCountDecision(c, s, iterations); + + ASSERT_TRUE(actual_count < (expected_count + variance)); + ASSERT_TRUE(actual_count > (expected_count - variance)); +} + +TEST(TraceIdRatioBasedSampler, TraceIdRatioBasedSamplerAll) +{ + double ratio = 1.0; + int iterations = 100000; + int expected_count = static_cast(iterations * ratio); + + trace_api::SpanContext c(true, true); + TraceIdRatioBasedSampler s(ratio); + + int actual_count = RunShouldSampleCountDecision(c, s, iterations); + + ASSERT_EQ(actual_count, expected_count); +} + +TEST(TraceIdRatioBasedSampler, TraceIdRatioBasedSamplerNone) +{ + double ratio = 0.0; + int iterations = 100000; + int expected_count = static_cast(iterations * ratio); + + trace_api::SpanContext c(true, true); + TraceIdRatioBasedSampler s(ratio); + + int actual_count = RunShouldSampleCountDecision(c, s, iterations); + + ASSERT_EQ(actual_count, expected_count); +} + +TEST(TraceIdRatioBasedSampler, GetDescription) +{ + TraceIdRatioBasedSampler s1(0.01); + ASSERT_EQ("TraceIdRatioBasedSampler{0.010000}", s1.GetDescription()); + + TraceIdRatioBasedSampler s2(0.00); + ASSERT_EQ("TraceIdRatioBasedSampler{0.000000}", s2.GetDescription()); + + TraceIdRatioBasedSampler s3(1.00); + ASSERT_EQ("TraceIdRatioBasedSampler{1.000000}", s3.GetDescription()); + + TraceIdRatioBasedSampler s4(0.102030405); + ASSERT_EQ("TraceIdRatioBasedSampler{0.102030}", s4.GetDescription()); + + TraceIdRatioBasedSampler s5(3.00); + ASSERT_EQ("TraceIdRatioBasedSampler{1.000000}", s5.GetDescription()); + + TraceIdRatioBasedSampler s6(-3.00); + ASSERT_EQ("TraceIdRatioBasedSampler{0.000000}", s6.GetDescription()); + + TraceIdRatioBasedSampler s7(1.00000000001); + ASSERT_EQ("TraceIdRatioBasedSampler{1.000000}", s7.GetDescription()); + + TraceIdRatioBasedSampler s8(-1.00000000001); + ASSERT_EQ("TraceIdRatioBasedSampler{0.000000}", s8.GetDescription()); + + TraceIdRatioBasedSampler s9(0.50); + ASSERT_EQ("TraceIdRatioBasedSampler{0.500000}", s9.GetDescription()); +} diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/tracer_provider_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/tracer_provider_test.cc new file mode 100644 index 000000000..498f66127 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/tracer_provider_test.cc @@ -0,0 +1,99 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/trace/tracer_provider.h" +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/sdk/trace/samplers/always_off.h" +#include "opentelemetry/sdk/trace/samplers/always_on.h" +#include "opentelemetry/sdk/trace/simple_processor.h" +#include "opentelemetry/sdk/trace/tracer.h" + +#include + +using namespace opentelemetry::sdk::trace; +using namespace opentelemetry::sdk::resource; + +#include + +TEST(TracerProvider, GetTracer) +{ + std::unique_ptr processor(new SimpleSpanProcessor(nullptr)); + std::vector> processors; + processors.push_back(std::move(processor)); + TracerProvider tp1(std::make_shared(std::move(processors), Resource::Create({}))); + auto t1 = tp1.GetTracer("test"); + auto t2 = tp1.GetTracer("test"); + auto t3 = tp1.GetTracer("different", "1.0.0"); + auto t4 = tp1.GetTracer(""); + auto t5 = tp1.GetTracer(opentelemetry::nostd::string_view{}); + auto t6 = tp1.GetTracer("different", "1.0.0", "https://opentelemetry.io/schemas/1.2.0"); + ASSERT_NE(nullptr, t1); + ASSERT_NE(nullptr, t2); + ASSERT_NE(nullptr, t3); + ASSERT_NE(nullptr, t6); + + // Should return the same instance each time. + ASSERT_EQ(t1, t2); + ASSERT_NE(t1, t3); + ASSERT_EQ(t4, t5); + ASSERT_NE(t3, t6); + + // Should be an sdk::trace::Tracer with the processor attached. +#ifdef OPENTELEMETRY_RTTI_ENABLED + auto sdkTracer1 = dynamic_cast(t1.get()); +#else + auto sdkTracer1 = static_cast(t1.get()); +#endif + ASSERT_NE(nullptr, sdkTracer1); + ASSERT_EQ("AlwaysOnSampler", sdkTracer1->GetSampler().GetDescription()); + std::unique_ptr processor2(new SimpleSpanProcessor(nullptr)); + std::vector> processors2; + processors2.push_back(std::move(processor2)); + TracerProvider tp2( + std::make_shared(std::move(processors2), Resource::Create({}), + std::unique_ptr(new AlwaysOffSampler()), + std::unique_ptr(new RandomIdGenerator))); +#ifdef OPENTELEMETRY_RTTI_ENABLED + auto sdkTracer2 = dynamic_cast(tp2.GetTracer("test").get()); +#else + auto sdkTracer2 = static_cast(tp2.GetTracer("test").get()); +#endif + ASSERT_EQ("AlwaysOffSampler", sdkTracer2->GetSampler().GetDescription()); + + auto instrumentation_library1 = sdkTracer1->GetInstrumentationLibrary(); + ASSERT_EQ(instrumentation_library1.GetName(), "test"); + ASSERT_EQ(instrumentation_library1.GetVersion(), ""); + + // Should be an sdk::trace::Tracer with the processor attached. +#ifdef OPENTELEMETRY_RTTI_ENABLED + auto sdkTracer3 = dynamic_cast(t3.get()); +#else + auto sdkTracer3 = static_cast(t3.get()); +#endif + auto instrumentation_library3 = sdkTracer3->GetInstrumentationLibrary(); + ASSERT_EQ(instrumentation_library3.GetName(), "different"); + ASSERT_EQ(instrumentation_library3.GetVersion(), "1.0.0"); +} + +TEST(TracerProvider, Shutdown) +{ + std::unique_ptr processor(new SimpleSpanProcessor(nullptr)); + std::vector> processors; + processors.push_back(std::move(processor)); + + TracerProvider tp1(std::make_shared(std::move(processors))); + + EXPECT_TRUE(tp1.Shutdown()); + + // It's safe to shutdown again + EXPECT_TRUE(tp1.Shutdown()); +} + +TEST(TracerProvider, ForceFlush) +{ + std::unique_ptr processor1(new SimpleSpanProcessor(nullptr)); + + TracerProvider tp1(std::move(processor1)); + + EXPECT_TRUE(tp1.ForceFlush()); +} diff --git a/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/tracer_test.cc b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/tracer_test.cc new file mode 100644 index 000000000..15a7566b9 --- /dev/null +++ b/src/jaegertracing/opentelemetry-cpp/sdk/test/trace/tracer_test.cc @@ -0,0 +1,731 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/trace/tracer.h" +#include "opentelemetry/exporters/memory/in_memory_span_exporter.h" +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/sdk/trace/samplers/always_off.h" +#include "opentelemetry/sdk/trace/samplers/always_on.h" +#include "opentelemetry/sdk/trace/samplers/parent.h" +#include "opentelemetry/sdk/trace/simple_processor.h" +#include "opentelemetry/sdk/trace/span_data.h" +#include "opentelemetry/trace/context.h" + +#include + +using namespace opentelemetry::sdk::trace; +using namespace opentelemetry::sdk::resource; +using opentelemetry::common::SteadyTimestamp; +using opentelemetry::common::SystemTimestamp; +namespace nostd = opentelemetry::nostd; +namespace common = opentelemetry::common; +using opentelemetry::common::KeyValueIterableView; +using opentelemetry::exporter::memory::InMemorySpanData; +using opentelemetry::exporter::memory::InMemorySpanExporter; +using opentelemetry::trace::SpanContext; + +/** + * A mock sampler with ShouldSample returning: + * Decision::RECORD_AND_SAMPLE if trace_id is valid + * Decision::DROP otherwise. + */ +class MockSampler final : public Sampler +{ +public: + SamplingResult ShouldSample( + const SpanContext & /*parent_context*/, + trace_api::TraceId trace_id, + nostd::string_view /*name*/, + trace_api::SpanKind /*span_kind*/, + const opentelemetry::common::KeyValueIterable & /*attributes*/, + const opentelemetry::trace::SpanContextKeyValueIterable & /*links*/) noexcept override + { + // Sample only if valid trace_id ( This is to test Sampler get's valid trace id) + if (trace_id.IsValid()) + { + // Return two pairs of attributes. These attributes should be added to the + // span attributes + return {Decision::RECORD_AND_SAMPLE, + nostd::unique_ptr>( + new const std::map( + {{"sampling_attr1", 123}, {"sampling_attr2", "string"}}))}; + } + else + { + // we should never reach here + assert(false); + return {Decision::DROP}; + } + } + + nostd::string_view GetDescription() const noexcept override { return "MockSampler"; } +}; + +/** + * A Mock Custom Id Generator + */ +class MockIdGenerator : public IdGenerator +{ + opentelemetry::trace::SpanId GenerateSpanId() noexcept override + { + return opentelemetry::trace::SpanId(buf_span); + } + + opentelemetry::trace::TraceId GenerateTraceId() noexcept override + { + return opentelemetry::trace::TraceId(buf_trace); + } + uint8_t buf_span[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + uint8_t buf_trace[16] = {1, 2, 3, 4, 5, 6, 7, 8, 8, 7, 6, 5, 4, 3, 2, 1}; +}; + +namespace +{ +std::shared_ptr initTracer(std::unique_ptr &&exporter) +{ + auto processor = std::unique_ptr(new SimpleSpanProcessor(std::move(exporter))); + std::vector> processors; + processors.push_back(std::move(processor)); + auto context = std::make_shared(std::move(processors)); + return std::shared_ptr(new Tracer(context)); +} + +std::shared_ptr initTracer( + std::unique_ptr &&exporter, + // For testing, just shove a pointer over, we'll take it over. + Sampler *sampler, + IdGenerator *id_generator = new RandomIdGenerator) +{ + auto processor = std::unique_ptr(new SimpleSpanProcessor(std::move(exporter))); + std::vector> processors; + processors.push_back(std::move(processor)); + auto resource = Resource::Create({}); + auto context = std::make_shared(std::move(processors), resource, + std::unique_ptr(sampler), + std::unique_ptr(id_generator)); + return std::shared_ptr(new Tracer(context)); +} + +} // namespace + +TEST(Tracer, ToInMemorySpanExporter) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); + + auto span_first = tracer->StartSpan("span 1"); + auto scope_first = tracer->WithActiveSpan(span_first); + auto span_second = tracer->StartSpan("span 2"); + + ASSERT_EQ(0, span_data->GetSpans().size()); + + span_second->End(); + + auto span2 = span_data->GetSpans(); + ASSERT_EQ(1, span2.size()); + ASSERT_EQ("span 2", span2.at(0)->GetName()); + EXPECT_TRUE(span2.at(0)->GetTraceId().IsValid()); + EXPECT_TRUE(span2.at(0)->GetSpanId().IsValid()); + EXPECT_TRUE(span2.at(0)->GetParentSpanId().IsValid()); + + span_first->End(); + + auto span1 = span_data->GetSpans(); + ASSERT_EQ(1, span1.size()); + ASSERT_EQ("span 1", span1.at(0)->GetName()); + EXPECT_TRUE(span1.at(0)->GetTraceId().IsValid()); + EXPECT_TRUE(span1.at(0)->GetSpanId().IsValid()); + EXPECT_FALSE(span1.at(0)->GetParentSpanId().IsValid()); + + // Verify trace and parent span id propagation + EXPECT_EQ(span1.at(0)->GetTraceId(), span2.at(0)->GetTraceId()); + EXPECT_EQ(span2.at(0)->GetParentSpanId(), span1.at(0)->GetSpanId()); +} + +TEST(Tracer, StartSpanSampleOn) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer_on = initTracer(std::move(exporter)); + + tracer_on->StartSpan("span 1")->End(); + + auto spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); + + auto &cur_span_data = spans.at(0); + ASSERT_LT(std::chrono::nanoseconds(0), cur_span_data->GetStartTime().time_since_epoch()); + ASSERT_LT(std::chrono::nanoseconds(0), cur_span_data->GetDuration()); +} + +TEST(Tracer, StartSpanSampleOff) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer_off = initTracer(std::move(exporter), new AlwaysOffSampler()); + + // This span will not be recorded. + auto span = tracer_off->StartSpan("span 2"); + + // Always generate a valid span-context (span-id) + auto context = span->GetContext(); + EXPECT_TRUE(context.IsValid()); + EXPECT_FALSE(context.IsSampled()); + + span->End(); + // The span doesn't write any span data because the sampling decision is alway + // DROP. + ASSERT_EQ(0, span_data->GetSpans().size()); +} + +TEST(Tracer, StartSpanCustomIdGenerator) +{ + IdGenerator *id_generator = new MockIdGenerator(); + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter), new AlwaysOnSampler(), id_generator); + + tracer->StartSpan("span 1")->End(); + auto spans = span_data->GetSpans(); + auto &cur_span_data = spans.at(0); + + EXPECT_EQ(cur_span_data->GetTraceId(), id_generator->GenerateTraceId()); + EXPECT_EQ(cur_span_data->GetSpanId(), id_generator->GenerateSpanId()); +} + +TEST(Tracer, StartSpanWithOptionsTime) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); + + opentelemetry::trace::StartSpanOptions start; + start.start_system_time = SystemTimestamp(std::chrono::nanoseconds(300)); + start.start_steady_time = SteadyTimestamp(std::chrono::nanoseconds(10)); + + opentelemetry::trace::EndSpanOptions end; + end.end_steady_time = SteadyTimestamp(std::chrono::nanoseconds(40)); + + tracer->StartSpan("span 1", start)->End(end); + + auto spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); + + auto &cur_span_data = spans.at(0); + ASSERT_EQ(std::chrono::nanoseconds(300), cur_span_data->GetStartTime().time_since_epoch()); + ASSERT_EQ(std::chrono::nanoseconds(30), cur_span_data->GetDuration()); +} + +TEST(Tracer, StartSpanWithAttributes) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); + + // Start a span with all supported scalar attribute types. + tracer + ->StartSpan("span 1", {{"attr1", "string"}, + {"attr2", false}, + {"attr1", 314159}, + {"attr3", (unsigned int)314159}, + {"attr4", (int32_t)-20}, + {"attr5", (uint32_t)20}, + {"attr6", (int64_t)-20}, + {"attr7", (uint64_t)20}, + {"attr8", 3.1}, + {"attr9", "string"}}) + ->End(); + + // Start a span with all supported array attribute types. + int listInt[] = {1, 2, 3}; + unsigned int listUInt[] = {1, 2, 3}; + int32_t listInt32[] = {1, -2, 3}; + uint32_t listUInt32[] = {1, 2, 3}; + int64_t listInt64[] = {1, -2, 3}; + uint64_t listUInt64[] = {1, 2, 3}; + double listDouble[] = {1.1, 2.1, 3.1}; + bool listBool[] = {true, false}; + nostd::string_view listStringView[] = {"a", "b"}; + std::map m; + m["attr1"] = nostd::span(listInt); + m["attr2"] = nostd::span(listUInt); + m["attr3"] = nostd::span(listInt32); + m["attr4"] = nostd::span(listUInt32); + m["attr5"] = nostd::span(listInt64); + m["attr6"] = nostd::span(listUInt64); + m["attr7"] = nostd::span(listDouble); + m["attr8"] = nostd::span(listBool); + m["attr9"] = nostd::span(listStringView); + + tracer->StartSpan("span 2", m)->End(); + + auto spans = span_data->GetSpans(); + ASSERT_EQ(2, spans.size()); + + auto &cur_span_data = spans.at(0); + ASSERT_EQ(9, cur_span_data->GetAttributes().size()); + ASSERT_EQ(314159, nostd::get(cur_span_data->GetAttributes().at("attr1"))); + ASSERT_EQ(false, nostd::get(cur_span_data->GetAttributes().at("attr2"))); + ASSERT_EQ(314159, nostd::get(cur_span_data->GetAttributes().at("attr3"))); + ASSERT_EQ(-20, nostd::get(cur_span_data->GetAttributes().at("attr4"))); + ASSERT_EQ(20, nostd::get(cur_span_data->GetAttributes().at("attr5"))); + ASSERT_EQ(-20, nostd::get(cur_span_data->GetAttributes().at("attr6"))); + ASSERT_EQ(20, nostd::get(cur_span_data->GetAttributes().at("attr7"))); + ASSERT_EQ(3.1, nostd::get(cur_span_data->GetAttributes().at("attr8"))); + ASSERT_EQ("string", nostd::get(cur_span_data->GetAttributes().at("attr9"))); + + auto &cur_span_data2 = spans.at(1); + ASSERT_EQ(9, cur_span_data2->GetAttributes().size()); + ASSERT_EQ(std::vector({1, 2, 3}), + nostd::get>(cur_span_data2->GetAttributes().at("attr1"))); + ASSERT_EQ(std::vector({1, 2, 3}), + nostd::get>(cur_span_data2->GetAttributes().at("attr2"))); + ASSERT_EQ(std::vector({1, -2, 3}), + nostd::get>(cur_span_data2->GetAttributes().at("attr3"))); + ASSERT_EQ(std::vector({1, 2, 3}), + nostd::get>(cur_span_data2->GetAttributes().at("attr4"))); + ASSERT_EQ(std::vector({1, -2, 3}), + nostd::get>(cur_span_data2->GetAttributes().at("attr5"))); + ASSERT_EQ(std::vector({1, 2, 3}), + nostd::get>(cur_span_data2->GetAttributes().at("attr6"))); + ASSERT_EQ(std::vector({1.1, 2.1, 3.1}), + nostd::get>(cur_span_data2->GetAttributes().at("attr7"))); + ASSERT_EQ(std::vector({true, false}), + nostd::get>(cur_span_data2->GetAttributes().at("attr8"))); + ASSERT_EQ(std::vector({"a", "b"}), + nostd::get>(cur_span_data2->GetAttributes().at("attr9"))); +} + +TEST(Tracer, StartSpanWithAttributesCopy) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); + + { + std::unique_ptr> numbers(new std::vector); + numbers->push_back(1); + numbers->push_back(2); + numbers->push_back(3); + + std::unique_ptr> strings(new std::vector); + std::string s1("a"); + std::string s2("b"); + std::string s3("c"); + strings->push_back(s1); + strings->push_back(s2); + strings->push_back(s3); + tracer + ->StartSpan("span 1", + {{"attr1", *numbers}, {"attr2", nostd::span(*strings)}}) + ->End(); + } + + auto spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); + + auto &cur_span_data = spans.at(0); + ASSERT_EQ(2, cur_span_data->GetAttributes().size()); + + auto numbers = nostd::get>(cur_span_data->GetAttributes().at("attr1")); + ASSERT_EQ(3, numbers.size()); + ASSERT_EQ(1, numbers[0]); + ASSERT_EQ(2, numbers[1]); + ASSERT_EQ(3, numbers[2]); + + auto strings = nostd::get>(cur_span_data->GetAttributes().at("attr2")); + ASSERT_EQ(3, strings.size()); + ASSERT_EQ("a", strings[0]); + ASSERT_EQ("b", strings[1]); + ASSERT_EQ("c", strings[2]); +} + +TEST(Tracer, GetSampler) +{ + auto resource = Resource::Create({}); + // Create a Tracer with a default AlwaysOnSampler + auto tracer_on = initTracer(nullptr); + +#ifdef OPENTELEMETRY_RTTI_ENABLED + auto &t1 = std::dynamic_pointer_cast(tracer_on)->GetSampler(); +#else + auto &t1 = std::static_pointer_cast(tracer_on)->GetSampler(); +#endif + ASSERT_EQ("AlwaysOnSampler", t1.GetDescription()); + + // Create a Tracer with a AlwaysOffSampler + auto tracer_off = initTracer(nullptr, new AlwaysOffSampler()); + +#ifdef OPENTELEMETRY_RTTI_ENABLED + auto &t2 = std::dynamic_pointer_cast(tracer_off)->GetSampler(); +#else + auto &t2 = std::static_pointer_cast(tracer_off)->GetSampler(); +#endif + ASSERT_EQ("AlwaysOffSampler", t2.GetDescription()); +} + +TEST(Tracer, SpanSetAttribute) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); + + auto span = tracer->StartSpan("span 1"); + + span->SetAttribute("abc", 3.1); + + span->End(); + + auto spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); + auto &cur_span_data = spans.at(0); + ASSERT_EQ(3.1, nostd::get(cur_span_data->GetAttributes().at("abc"))); +} + +TEST(Tracer, TestAfterEnd) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); + auto span = tracer->StartSpan("span 1"); + span->SetAttribute("abc", 3.1); + + span->End(); + + // test after end + span->SetAttribute("testing null recordable", 3.1); + span->AddEvent("event 1"); + span->AddEvent("event 2", std::chrono::system_clock::now()); + span->AddEvent("event 3", std::chrono::system_clock::now(), {{"attr1", 1}}); + std::string new_name{"new name"}; + span->UpdateName(new_name); + span->SetAttribute("attr1", 3.1); + std::string description{"description"}; + span->SetStatus(opentelemetry::trace::StatusCode::kError, description); + span->End(); + + auto spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); + auto &cur_span_data = spans.at(0); + ASSERT_EQ(3.1, nostd::get(cur_span_data->GetAttributes().at("abc"))); +} + +TEST(Tracer, SpanSetEvents) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); + + auto span = tracer->StartSpan("span 1"); + span->AddEvent("event 1"); + span->AddEvent("event 2", std::chrono::system_clock::now()); + span->AddEvent("event 3", std::chrono::system_clock::now(), {{"attr1", 1}}); + span->End(); + + auto spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); + + auto &span_data_events = spans.at(0)->GetEvents(); + ASSERT_EQ(3, span_data_events.size()); + ASSERT_EQ("event 1", span_data_events[0].GetName()); + ASSERT_EQ("event 2", span_data_events[1].GetName()); + ASSERT_EQ("event 3", span_data_events[2].GetName()); + ASSERT_EQ(0, span_data_events[0].GetAttributes().size()); + ASSERT_EQ(0, span_data_events[1].GetAttributes().size()); + ASSERT_EQ(1, span_data_events[2].GetAttributes().size()); +} + +TEST(Tracer, SpanSetLinks) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); + + { + + // Single span link passed through Initialization list + tracer->StartSpan("efg", {{"attr1", 1}}, {{SpanContext(false, false), {{"attr2", 2}}}})->End(); + auto spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); + + auto &span_data_links = spans.at(0)->GetLinks(); + ASSERT_EQ(1, span_data_links.size()); + auto link = span_data_links.at(0); + ASSERT_EQ(nostd::get(link.GetAttributes().at("attr2")), 2); + } + { + + // Multiple span links passed through Initialization list + tracer + ->StartSpan("efg", {{"attr1", 1}}, + {{SpanContext(false, false), {{"attr2", 2}}}, + {SpanContext(false, false), {{"attr3", 3}}}}) + ->End(); + auto spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); + + auto &span_data_links = spans.at(0)->GetLinks(); + ASSERT_EQ(2, span_data_links.size()); + auto link1 = span_data_links.at(0); + ASSERT_EQ(nostd::get(link1.GetAttributes().at("attr2")), 2); + auto link2 = span_data_links.at(1); + ASSERT_EQ(nostd::get(link2.GetAttributes().at("attr3")), 3); + } + + { + + // Multiple links, each with multiple attributes passed through Initialization list + tracer + ->StartSpan("efg", {{"attr1", 1}}, + {{SpanContext(false, false), {{"attr2", 2}, {"attr3", 3}}}, + {SpanContext(false, false), {{"attr4", 4}}}}) + ->End(); + auto spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); + + auto &span_data_links = spans.at(0)->GetLinks(); + ASSERT_EQ(2, span_data_links.size()); + auto link1 = span_data_links.at(0); + ASSERT_EQ(nostd::get(link1.GetAttributes().at("attr2")), 2); + ASSERT_EQ(nostd::get(link1.GetAttributes().at("attr3")), 3); + auto link2 = span_data_links.at(1); + ASSERT_EQ(nostd::get(link2.GetAttributes().at("attr4")), 4); + } + + { + std::map attrs1 = {{"attr1", "1"}, {"attr2", "2"}}; + std::map attrs2 = {{"attr3", "3"}, {"attr4", "4"}}; + + std::vector>> links = { + {SpanContext(false, false), attrs1}, {SpanContext(false, false), attrs2}}; + tracer->StartSpan("efg", attrs1, links)->End(); + auto spans = span_data->GetSpans(); + + auto &span_data_links = spans.at(0)->GetLinks(); + ASSERT_EQ(2, span_data_links.size()); + auto link1 = span_data_links.at(0); + ASSERT_EQ(nostd::get(link1.GetAttributes().at("attr1")), "1"); + ASSERT_EQ(nostd::get(link1.GetAttributes().at("attr2")), "2"); + auto link2 = span_data_links.at(1); + ASSERT_EQ(nostd::get(link2.GetAttributes().at("attr3")), "3"); + ASSERT_EQ(nostd::get(link2.GetAttributes().at("attr4")), "4"); + } +} + +TEST(Tracer, TestAlwaysOnSampler) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer_on = initTracer(std::move(exporter)); + + // Testing AlwaysOn sampler. + // Create two spans for each tracer. Check the exported result. + auto span_on_1 = tracer_on->StartSpan("span 1"); + auto span_on_2 = tracer_on->StartSpan("span 2"); + span_on_2->End(); + span_on_1->End(); + + auto spans = span_data->GetSpans(); + ASSERT_EQ(2, spans.size()); + ASSERT_EQ("span 2", spans.at(0)->GetName()); // span 2 ends first. + ASSERT_EQ("span 1", spans.at(1)->GetName()); +} + +TEST(Tracer, TestAlwaysOffSampler) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer_off = initTracer(std::move(exporter), new AlwaysOffSampler()); + auto span_off_1 = tracer_off->StartSpan("span 1"); + auto span_off_2 = tracer_off->StartSpan("span 2"); + + span_off_1->SetAttribute("attr1", 3.1); // Not recorded. + + span_off_2->End(); + span_off_1->End(); + + // The tracer export nothing with an AlwaysOff sampler + ASSERT_EQ(0, span_data->GetSpans().size()); +} + +TEST(Tracer, TestParentBasedSampler) +{ + // Current ShouldSample always pass an empty ParentContext, + // so this sampler will work as an AlwaysOnSampler. + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data_parent_on = exporter->GetData(); + auto tracer_parent_on = + initTracer(std::move(exporter), new ParentBasedSampler(std::make_shared())); + + auto span_parent_on_1 = tracer_parent_on->StartSpan("span 1"); + auto span_parent_on_2 = tracer_parent_on->StartSpan("span 2"); + + span_parent_on_1->SetAttribute("attr1", 3.1); + + span_parent_on_2->End(); + span_parent_on_1->End(); + + auto spans = span_data_parent_on->GetSpans(); + ASSERT_EQ(2, spans.size()); + ASSERT_EQ("span 2", spans.at(0)->GetName()); + ASSERT_EQ("span 1", spans.at(1)->GetName()); + + // Current ShouldSample always pass an empty ParentContext, + // so this sampler will work as an AlwaysOnSampler. + std::unique_ptr exporter2(new InMemorySpanExporter()); + std::shared_ptr span_data_parent_off = exporter2->GetData(); + auto tracer_parent_off = + initTracer(std::move(exporter2), + // Add this to avoid different results for old and new version of clang-format + new ParentBasedSampler(std::make_shared())); + + auto span_parent_off_1 = tracer_parent_off->StartSpan("span 1"); + auto span_parent_off_2 = tracer_parent_off->StartSpan("span 2"); + + span_parent_off_1->SetAttribute("attr1", 3.1); + + span_parent_off_1->End(); + span_parent_off_2->End(); + ASSERT_EQ(0, span_data_parent_off->GetSpans().size()); +} + +TEST(Tracer, WithActiveSpan) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); + auto spans = span_data.get()->GetSpans(); + + ASSERT_EQ(0, spans.size()); + + { + auto span_first = tracer->StartSpan("span 1"); + auto scope_first = tracer->WithActiveSpan(span_first); + + { + auto span_second = tracer->StartSpan("span 2"); + auto scope_second = tracer->WithActiveSpan(span_second); + + spans = span_data->GetSpans(); + ASSERT_EQ(0, spans.size()); + + span_second->End(); + } + + spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); + EXPECT_EQ("span 2", spans.at(0)->GetName()); + + span_first->End(); + } + + spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); + EXPECT_EQ("span 1", spans.at(0)->GetName()); +} + +TEST(Tracer, ExpectParent) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); + auto spans = span_data.get()->GetSpans(); + + ASSERT_EQ(0, spans.size()); + + auto span_first = tracer->StartSpan("span 1"); + + trace_api::StartSpanOptions options; + options.parent = span_first->GetContext(); + auto span_second = tracer->StartSpan("span 2", options); + + options.parent = span_second->GetContext(); + auto span_third = tracer->StartSpan("span 3", options); + + span_third->End(); + span_second->End(); + span_first->End(); + + spans = span_data->GetSpans(); + ASSERT_EQ(3, spans.size()); + auto spandata_first = std::move(spans.at(2)); + auto spandata_second = std::move(spans.at(1)); + auto spandata_third = std::move(spans.at(0)); + EXPECT_EQ("span 1", spandata_first->GetName()); + EXPECT_EQ("span 2", spandata_second->GetName()); + EXPECT_EQ("span 3", spandata_third->GetName()); + + EXPECT_EQ(spandata_first->GetSpanId(), spandata_second->GetParentSpanId()); + EXPECT_EQ(spandata_second->GetSpanId(), spandata_third->GetParentSpanId()); +} + +TEST(Tracer, ExpectParentAsContext) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); + auto spans = span_data.get()->GetSpans(); + + ASSERT_EQ(0, spans.size()); + + auto span_first = tracer->StartSpan("span 1"); + + opentelemetry::context::Context c1; + auto c2 = trace_api::SetSpan(c1, span_first); + trace_api::StartSpanOptions options; + options.parent = c2; + auto span_second = tracer->StartSpan("span 2", options); + + auto c3 = trace_api::SetSpan(c2, span_second); + options.parent = c3; + auto span_third = tracer->StartSpan("span 3", options); + + span_third->End(); + span_second->End(); + span_first->End(); + + spans = span_data->GetSpans(); + ASSERT_EQ(3, spans.size()); + auto spandata_first = std::move(spans.at(2)); + auto spandata_second = std::move(spans.at(1)); + auto spandata_third = std::move(spans.at(0)); + EXPECT_EQ("span 1", spandata_first->GetName()); + EXPECT_EQ("span 2", spandata_second->GetName()); + EXPECT_EQ("span 3", spandata_third->GetName()); + + EXPECT_EQ(spandata_first->GetSpanId(), spandata_second->GetParentSpanId()); + EXPECT_EQ(spandata_second->GetSpanId(), spandata_third->GetParentSpanId()); +} + +TEST(Tracer, ValidTraceIdToSampler) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter), new MockSampler()); + + auto span = tracer->StartSpan("span 1"); + // sampler was fed with valid trace_id, so span shouldn't be NoOp Span. + EXPECT_TRUE(span->IsRecording()); + EXPECT_TRUE(span->GetContext().IsValid()); +} + +TEST(Tracer, SpanCleanupWithScope) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); + { + auto span0 = tracer->StartSpan("Span0"); + auto span1 = tracer->StartSpan("span1"); + { + trace_api::Scope scope(span1); + auto span2 = tracer->StartSpan("span2"); + { + trace_api::Scope scope(span2); + auto span3 = tracer->StartSpan("span3"); + } + } + } + EXPECT_EQ(4, span_data->GetSpans().size()); +} -- cgit v1.2.3