diff --git a/CHANGELOG.md b/CHANGELOG.md index 85bdbadc28..d2d28461cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,8 @@ Increment the: [#2388](https://github.com/open-telemetry/opentelemetry-cpp/pull/2388) * [SEMANTIC CONVENTION] Upgrade to semconv 1.23.1 [#2428](https://github.com/open-telemetry/opentelemetry-cpp/pull/2428) +* [REMOVAL] Remove ZPAGES + [#2433](https://github.com/open-telemetry/opentelemetry-cpp/pull/2433) Important changes: @@ -103,6 +105,11 @@ Breaking changes: * Please check configuration variables, to make sure `_LOGS_` variables are set as expected. +* [REMOVAL] Remove ZPAGES + [#2433](https://github.com/open-telemetry/opentelemetry-cpp/pull/2433) + * As announced in release 1.12.0, + the deprecated ZPAGES exporter is now removed. + ## [1.12.0] 2023-10-16 * [BUILD] Support `pkg-config` diff --git a/CMakeLists.txt b/CMakeLists.txt index f795eadbb7..c27a910f4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -212,9 +212,6 @@ option(WITH_PROMETHEUS "Whether to include the Prometheus Client in the SDK" option(WITH_ELASTICSEARCH "Whether to include the Elasticsearch Client in the SDK" OFF) -option(WITH_ZPAGES - "DEPRECATED - Whether to include the Zpages Server in the SDK" OFF) - option(WITH_NO_GETENV "Whether the platform supports environment variables" OFF) option(BUILD_TESTING "Whether to enable tests" ON) @@ -290,14 +287,6 @@ option(WITH_OTLP_HTTP_SSL_TLS_PREVIEW option(WITH_METRICS_EXEMPLAR_PREVIEW "Whether to enable exemplar within metrics" OFF) -if(WITH_ZPAGES) - if(WITH_NO_DEPRECATED_CODE) - message(FATAL_ERROR "WITH_ZPAGES is DEPRECATED.") - else() - message(WARNING "WITH_ZPAGES is DEPRECATED.") - endif() -endif() - # # Verify options dependencies # @@ -480,7 +469,6 @@ endif() if(WITH_ELASTICSEARCH OR WITH_ZIPKIN OR WITH_OTLP_HTTP - OR WITH_ZPAGES OR BUILD_W3CTRACECONTEXT_TEST OR WITH_ETW) set(USE_NLOHMANN_JSON ON) diff --git a/DEPRECATED.md b/DEPRECATED.md index c9ad356696..0632f50503 100644 --- a/DEPRECATED.md +++ b/DEPRECATED.md @@ -92,89 +92,7 @@ N/A ## [opentelemetry-cpp Exporter] -### ZPages exporter - -#### Announcement (ZPages) - -* Version: 1.11.0 -* Date: 2023-09-01 -* PR: [DEPRECATION] Deprecate ZPAGES - [#2291](https://github.com/open-telemetry/opentelemetry-cpp/pull/2291) - -#### Motivation (ZPages) - -The ZPages specification itself was introduced in 2020, -and has been experimental ever since, -not getting a lot of attention, and never reaching a stable status. - -Several other opentelemetry projects have implemented support for zpages, -only to later deprecate and then remove the zpages implementation (Java, -C#), abandoning the zpages feature. - -In this context, it does not make sense to continue to maintain the zpages -code in opentelemetry-cpp. - -#### Scope (ZPages) - -The following are deprecated and planned for removal: - -* all the API headers located under - `ext/include/opentelemetry/ext/zpages/`, including: - * the C++ class `ThreadsafeSpanData` - * the C++ class `TracezDataAggregator` - * the C++ class `TracezHttpServer` - * the C++ class `TracezSpanProcessor` - * the C++ class `TracezSharedData` - * the C++ class `ZPages` - * the C++ class `zPagesHttpServer` -* all the code and doc located under `ext/src/zpages/` -* all the tests located under `ext/test/zpages/` -* the zpages exporter library(`opentelemetry_zpages`) -* the zpages build options in CMake (`WITH_ZPAGES`) - -The following code is no longer considered public, will no longer be -installed, and will no longer be useable outside of -the opentelemetry-cpp implementation: - -* all the API headers located under - `ext/include/opentelemetry/ext/http/server`, including: - * the C++ class `FileHttpServer` - * the C++ class `HttpRequestCallback` - * the C++ class `HttpServer` - * the C++ class `HttpRequestHandler` - * the C++ class `SocketCallback` - -This implementation of an HTTP server is meant to be used for testing only, -it is not production ready. - -#### Mitigation (ZPages) - -Consider using a different exporter, -for example the OTLP exporter (both OTLP HTTP and OTLP GRPC are supported), -to expose trace data to a separate trace backend. - -Note that this changes the access pattern: - -* with zpages, data is only available locally (in process) -* with other exporters, data is available externally (not in process) - -Our assessment is that the zpages implementation is no longer in use, -and can be removed. - -If that assessment is incorrect (i.e., if you own a project that depends -on the zpage exporter from opentelemetry-cpp), please comment on the -removal issue -[#2292](https://github.com/open-telemetry/opentelemetry-cpp/issues/2292). - -An alternative to zpage removal is to move the code to the -opentelemetry-cpp-contrib github -[repository](https://github.com/open-telemetry/opentelemetry-cpp-contrib). - -Contributions to migrate the code, and maintain zpages, are welcome. - -#### Planned removal (ZPages) - -* Date: December, 2023 +N/A ## [Documentation] diff --git a/INSTALL.md b/INSTALL.md index 68fefff1fe..9cd9cef6e2 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -124,10 +124,6 @@ You can link OpenTelemetry C++ SDK with libraries provided in -- Installing: //lib/cmake/opentelemetry-cpp/opentelemetry-cpp-config.cmake -- Installing: //lib/cmake/opentelemetry-cpp/opentelemetry-cpp-config-version.cmake ... - -- Installing: //include/opentelemetry//ext/zpages/static/tracez_index.h - -- Installing: //include/opentelemetry//ext/zpages/static/tracez_style.h - -- Installing: //include/opentelemetry//ext/zpages/threadsafe_span_data.h - -- Installing: //lib/libopentelemetry_zpages.a $ ``` diff --git a/ci/do_ci.sh b/ci/do_ci.sh index f679c6af04..de03a2585e 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -459,8 +459,7 @@ elif [[ "$1" == "bazel.asan" ]]; then elif [[ "$1" == "bazel.tsan" ]]; then # TODO - potential race condition in Civetweb server used by prometheus-cpp during shutdown # https://github.com/civetweb/civetweb/issues/861, so removing prometheus from the test -# zpages test failing with tsan. Ignoring the tests for now, as zpages will be removed soon. - bazel $BAZEL_STARTUP_OPTIONS test --config=tsan $BAZEL_TEST_OPTIONS_ASYNC -- //... -//exporters/prometheus/... -//ext/test/zpages/... + bazel $BAZEL_STARTUP_OPTIONS test --config=tsan $BAZEL_TEST_OPTIONS_ASYNC -- //... -//exporters/prometheus/... exit 0 elif [[ "$1" == "bazel.valgrind" ]]; then bazel $BAZEL_STARTUP_OPTIONS build $BAZEL_OPTIONS_ASYNC //... diff --git a/cmake/opentelemetry-cpp-config.cmake.in b/cmake/opentelemetry-cpp-config.cmake.in index 5c450c1df6..86098f7d7c 100644 --- a/cmake/opentelemetry-cpp-config.cmake.in +++ b/cmake/opentelemetry-cpp-config.cmake.in @@ -46,7 +46,6 @@ # opentelemetry-cpp::ostream_span_exporter - Imported target of opentelemetry-cpp::ostream_span_exporter # opentelemetry-cpp::elasticsearch_log_record_exporter - Imported target of opentelemetry-cpp::elasticsearch_log_record_exporter # opentelemetry-cpp::etw_exporter - Imported target of opentelemetry-cpp::etw_exporter -# opentelemetry-cpp::zpages - Imported target of opentelemetry-cpp::zpages # opentelemetry-cpp::http_client_curl - Imported target of opentelemetry-cpp::http_client_curl # opentelemetry-cpp::opentracing_shim - Imported target of opentelemetry-cpp::opentracing_shim # @@ -100,7 +99,6 @@ set(_OPENTELEMETRY_CPP_LIBRARIES_TEST_TARGETS prometheus_exporter elasticsearch_log_record_exporter etw_exporter - zpages http_client_curl opentracing_shim) foreach(_TEST_TARGET IN LISTS _OPENTELEMETRY_CPP_LIBRARIES_TEST_TARGETS) diff --git a/docs/dependencies.md b/docs/dependencies.md index 2f2f2368f6..da6c584fe8 100644 --- a/docs/dependencies.md +++ b/docs/dependencies.md @@ -92,9 +92,6 @@ Both these dependencies are listed here: - `libcurl` for connecting with Elasticsearch server over HTTP protocol. - `nlohmann/json` for encoding Elastic Search messages. -- [Zpages](/ext/src/zpages): - - None - - [Opentracing](/opentracing-shim) shim: - [`opentracing-cpp`](https://github.com/opentracing/opentracing-cpp) diff --git a/examples/zpages/BUILD b/examples/zpages/BUILD deleted file mode 100644 index 529c839cb7..0000000000 --- a/examples/zpages/BUILD +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright The OpenTelemetry Authors -# SPDX-License-Identifier: Apache-2.0 - -package(default_visibility = ["//visibility:public"]) - -cc_binary( - name = "zpages_example", - srcs = [ - "zpages_example.cc", - ], - linkopts = select({ - "//bazel:windows": [], - "//conditions:default": ["-pthread"], - }), - tags = ["examples"], - deps = [ - "//ext:headers", - "//ext/src/zpages", - "//sdk/src/trace", - ], -) diff --git a/examples/zpages/zpages_example.cc b/examples/zpages/zpages_example.cc deleted file mode 100644 index 2691a73158..0000000000 --- a/examples/zpages/zpages_example.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -/** - * This is a basic example for zpages that helps users get familiar with how to - * use this feature in OpenTelemetery - */ -#include -#include -#include - -#include "opentelemetry/ext/zpages/zpages.h" // Required file include for zpages - -using opentelemetry::common::SteadyTimestamp; -namespace trace_api = opentelemetry::trace; - -int main(int argc, char *argv[]) -{ - - /** - * The following line initializes zPages and starts a webserver at - * http://localhost:30000/tracez/ where spans that are created in the application - * can be viewed. - * Note that the webserver is destroyed after the application ends execution. - */ - ZPages::Initialize(); - auto tracer = trace_api::Provider::GetTracerProvider()->GetTracer(""); - - std::cout << "This example for zPages creates a few types of spans and then " - << "creates a span every second for the duration of the application" - << "\n"; - - // Error span - std::map attribute_map; - attribute_map["completed_search_for"] = "Unknown user"; - tracer->StartSpan("find user", attribute_map) - ->SetStatus(trace_api::StatusCode::kError, "User not found"); - - // Long time duration span - std::map attribute_map2; - attribute_map2["completed_search_for"] = "John Doe"; - trace_api::StartSpanOptions start; - start.start_steady_time = SteadyTimestamp(nanoseconds(1)); - trace_api::EndSpanOptions end; - end.end_steady_time = SteadyTimestamp(nanoseconds(1000000000000)); - tracer->StartSpan("find user", attribute_map2, start)->End(end); - - // Running(deadlock) span - std::map attribute_map3; - attribute_map3["searching_for"] = "Deleted user"; - auto running_span = tracer->StartSpan("find user", attribute_map3); - - // Create a completed span every second till user stops the loop - std::cout << "Presss CTRL+C to stop...\n"; - while (true) - { - std::this_thread::sleep_for(seconds(1)); - tracer->StartSpan("ping user")->End(); - } -} diff --git a/ext/include/opentelemetry/ext/zpages/latency_boundaries.h b/ext/include/opentelemetry/ext/zpages/latency_boundaries.h deleted file mode 100644 index cc03b169b2..0000000000 --- a/ext/include/opentelemetry/ext/zpages/latency_boundaries.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include - -#include "opentelemetry/version.h" - -using std::chrono::microseconds; -using std::chrono::milliseconds; -using std::chrono::nanoseconds; -using std::chrono::seconds; - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace ext -{ -namespace zpages -{ -/** - * kLatencyBoundaries is a constant array that contains the 9 latency - * boundaries. Each value in the array represents the lower limit(inclusive) of - * the boundary(in nano seconds) and the upper limit(exclusive) of the boundary - * is the lower limit of the next one. The upper limit of the last boundary is - * INF. - */ -const std::array kLatencyBoundaries = { - nanoseconds(0), - nanoseconds(microseconds(10)), - nanoseconds(microseconds(100)), - nanoseconds(milliseconds(1)), - nanoseconds(milliseconds(10)), - nanoseconds(milliseconds(100)), - nanoseconds(seconds(1)), - nanoseconds(seconds(10)), - nanoseconds(seconds(100)), -}; - -/** - * LatencyBoundary enum is used to index into the kLatencyBoundaries container. - * Using this enum lets you access the latency boundary at each index without - * using magic numbers - */ -enum LatencyBoundary -{ - k0MicroTo10Micro, - k10MicroTo100Micro, - k100MicroTo1Milli, - k1MilliTo10Milli, - k10MilliTo100Milli, - k100MilliTo1Second, - k1SecondTo10Second, - k10SecondTo100Second, - k100SecondToMax -}; - -} // namespace zpages -} // namespace ext -OPENTELEMETRY_END_NAMESPACE diff --git a/ext/include/opentelemetry/ext/zpages/static/tracez_index.h b/ext/include/opentelemetry/ext/zpages/static/tracez_index.h deleted file mode 100644 index c4c5b4933d..0000000000 --- a/ext/include/opentelemetry/ext/zpages/static/tracez_index.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -const char tracez_index[] = - "" - "" - "" - " " - " zPages TraceZ" - " " - " " - " " - " " - "

zPages TraceZ

" - " Data last fetched:
" - "
" - "

" - "
" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - "
Span NameError SamplesRunningLatency Samples
" - " " - "
" - "
Row count: 0
" - "
" - "
" - "
" - " " - "
" - " " - "
" - "
Row count: 0
" - "
" - " " - ""; diff --git a/ext/include/opentelemetry/ext/zpages/static/tracez_script.h b/ext/include/opentelemetry/ext/zpages/static/tracez_script.h deleted file mode 100644 index b21ceea8ee..0000000000 --- a/ext/include/opentelemetry/ext/zpages/static/tracez_script.h +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -const char tracez_script[] = - "" - "window.onload = () => refreshData();" - "" - "const latencies = [" - " '>0s', '>10µs', '>100µs'," - " '>1ms', '>10ms', '>100ms'," - " '>1s', '>10s', '>100s'," - "];" - "" - "const statusCodeDescriptions = {" - " 'OK': 'The operation completed successfully.'," - " 'CANCELLED': 'The operation was cancelled (typically by the caller).'," - " 'UNKNOWN': `Unknown error. An example of where this error may be returned is if a Status " - "value received" - " from another address space belongs to an error-space that is not known in this " - "address space." - " Also errors raised by APIs that do not return enough error information may be " - "converted to" - " this error.`," - " 'INVALID_ARGUMENT': `Client specified an invalid argument. Note that this differs from " - "FAILED_PRECONDITION." - " INVALID_ARGUMENT indicates arguments that are problematic regardless of the state " - "of the" - " system (e.g., a malformed file name).`," - " 'DEADLINE_EXCEEDED': `Deadline expired before operation could complete. For operations that " - "change the state of the" - " system, this error may be returned even if the operation has completed " - "successfully. For" - " example, a successful response from a server could have been delayed long enough " - "for the" - " deadline to expire.`," - " 'NOT_FOUND' : 'Some requested entity (e.g., file or directory) was not found.'," - " 'ALREADY_EXISTS': 'Some entity that we attempted to create (e.g., file or directory) " - "already exists.'," - " 'PERMISSION_DENIED': `The caller does not have permission to execute the specified " - "operation. PERMISSION_DENIED" - " must not be used for rejections caused by exhausting some resource (use " - "RESOURCE_EXHAUSTED" - " instead for those errors). PERMISSION_DENIED must not be used if the caller cannot " - "be" - " identified (use UNAUTHENTICATED instead for those errors).`," - " 'RESOURCE_EXHAUSTED': `Some resource has been exhausted, perhaps a per-user quota, or " - "perhaps the entire file system" - " is out of space.`," - " 'FAILED_PRECONDITION': `Operation was rejected because the system is not in a state " - "required for the operation's" - " execution. For example, directory to be deleted may be non-empty, an rmdir " - "operation is" - " applied to a non-directory, etc.`," - " 'ABORTED': `The operation was aborted, typically due to a concurrency issue like sequencer " - "check" - " failures, transaction aborts, etc`," - " 'OUT_OF_RANGE': `Operation was attempted past the valid range. E.g., seeking or reading " - "past end of file.`," - " 'UNIMPLEMENTED': 'Operation is not implemented or not supported/enabled in this service.'," - " 'INTERNAL': `Internal errors. Means some invariants expected by underlying system has been " - "broken. If you" - " see one of these errors, something is very broken.`," - " 'UNAVAILABLE': `The service is currently unavailable. This is a most likely a transient " - "condition and may be" - " corrected by retrying with a backoff.`," - " 'DATA_LOSS': 'Unrecoverable data loss or corruption.'," - " 'UNAUTHENTICATED': 'The request does not have valid authentication credentials for the " - "operation.'," - "};" - "" - "const units = {'duration': 'ns'};" - "" - "" - "const details = {'status': statusCodeDescriptions};" - "" - "/* Latency info is returned as an array, so they need to be parsed accordingly */" - "const getLatencyCell = (span, i, h) => `${span[h][i]}`;" - "" - "/* Pretty print a cell with a map */" - "const getKeyValueCell = (span, h) => `" - " ${JSON.stringify(span[h], null, 2)}" - " `;" - "" - "/* Standard categories when checking span details */" - "const idCols = ['spanid', 'parentid', 'traceid'];" - "const detailCols = ['attributes']; /* Columns error, running, and latency spans all share */" - "const dateCols = ['start']; /* Categories to change to date */" - "const numCols = ['duration']; /* Categories to change to num */" - "const clickCols = ['error', 'running']; /* Non-latency clickable cols */" - "const arrayCols = { " - " 'latency': getLatencyCell," - " 'events': getKeyValueCell," - " 'attributes': getKeyValueCell" - "};" - "" - "const base_endpt = '/tracez/get/'; /* For making GET requests */" - "" - "/* Maps table types to their approporiate formatting */" - "const tableFormatting = {" - " 'all': {" - " 'url': base_endpt + 'aggregations'," - " 'html_id': 'overview_table'," - " 'sizing': [" - " {'sz': 'md', 'repeats': 1}," - " {'sz': 'sm', 'repeats': 11}," - " ]," - " 'headings': ['name', ...clickCols, 'latency']," - " 'cell_headings': ['name', ...clickCols, ...latencies]," - " }," - " 'error': {" - " 'url': base_endpt + 'error/'," - " 'html_id': 'name_type_detail_table'," - " 'sizing': [" - " {'sz': 'sm', 'repeats': 5}," - " {'sz': 'sm-md', 'repeats': 1}," - " ]," - " 'headings': [...idCols, ...dateCols, 'status', ...detailCols]," - " 'has_subheading': true," - " }," - " 'running': {" - " 'url': base_endpt + 'running/'," - " 'html_id': 'name_type_detail_table'," - " 'sizing': [" - " {'sz': 'sm', 'repeats': 4}," - " {'sz': 'sm-md', 'repeats': 1}," - " ]," - " 'headings': [...idCols, ...dateCols, ...detailCols]," - " 'has_subheading': true," - " 'status': 'pending'," - " }," - " 'latency': {" - " 'url': base_endpt + 'latency/'," - " 'html_id': 'name_type_detail_table'," - " 'sizing': [" - " {'sz': 'sm', 'repeats': 5}," - " {'sz': 'sm-md', 'repeats': 1}," - " ]," - " 'headings': [...idCols, ...dateCols, ...numCols, ...detailCols]," - " 'has_subheading': true," - " 'status': 'ok'" - " }" - "};" - "const getFormat = group => tableFormatting[group];" - "" - "/* Getters using formatting config variable */" - "const getURL = group => getFormat(group)['url'];" - "const getHeadings = group => getFormat(group)['headings'];" - "const getCellHeadings = group => 'cell_headings' in getFormat(group)" - " ? getFormat(group)['cell_headings'] : getHeadings(group); " - "const getSizing = group => getFormat(group)['sizing'];" - "const getStatus = group => isLatency(group) ? 'ok' : getFormat(group)['status'];" - "const getHTML = group => getFormat(group)['html_id'];" - "" - "const isDate = col => new Set(dateCols).has(col);" - "const isLatency = group => !(new Set(clickCols).has(group)); /* non latency clickable cols, " - "change to include latency? */" - "const isArrayCol = group => (new Set(Object.keys(arrayCols)).has(group));" - "const hasCallback = col => new Set(clickCols).has(col); /* Non-latency cb columns */" - "const hideHeader = h => new Set([...clickCols, 'name']).has(h); /* Headers to not show render " - "twice */" - "const hasSubheading = group => isLatency(group) || 'has_subheading' in getFormat(group); " - "const hasStatus = group => isLatency(group) || 'status' in getFormat(group);" - "" - "const toTitlecase = word => word.charAt(0).toUpperCase() + word.slice(1);" - "const updateLastRefreshStr = () => document.getElementById('lastUpdateTime').innerHTML = new " - "Date().toLocaleString();" - "" - "const getStatusHTML = group => !hasStatus(group) ? ''" - " : `All of these spans have status code ${getStatus(group)}`;" - "" - "/* Returns an HTML string that handlles width formatting" - " for a table group */" - "const tableSizing = group => ''" - " + getSizing(group).map(sz =>" - " (``).repeat(sz['repeats']))" - " .join('')" - " + '';" - "" - "/* Returns an HTML string for a table group's headings," - " hiding headings where needed */" - "const tableHeadings = group => ''" - " + getCellHeadings(group).map(h => `${(hideHeader(h) ? '' : h)}`).join('')" - " + '';" - "" - "/* Returns an HTML string, which represents the formatting for" - " the entire header for a table group. This doesn't change, and" - " includes the width formatting and the actual table headers */" - "const tableHeader = group => tableSizing(group) + tableHeadings(group);" - "" - "/* Return formatting for an array-based value based on its header */" - "const getArrayCells = (h, span) => span[h].length" - " ? (span[h].map((_, i) => arrayCols[h](span, i, h))).join('')" - " : (Object.keys(span[h]).length ? arrayCols[h](span, h) : `${emptyContent()}`);" - "" - "const emptyContent = () => `(not set)`;" - "" - "const dateStr = nanosec => {" - " const mainDate = new Date(nanosec / 1000000).toLocaleString();" - " let lostPrecision = String(nanosec % 1000000);" - " while (lostPrecision.length < 6) lostPrecision = 0 + lostPrecision;" - " const endingLocation = mainDate.indexOf('M') - 2;" - " return `${mainDate.substr(0, " - "endingLocation)}:${lostPrecision}${mainDate.substr(endingLocation)}`;" - "};" - "" - "const detailCell = (h, span) => {" - " const detailKey = Object.keys(details[h])[span[h]];" - " const detailVal = details[h][detailKey];" - " return `" - " ${detailKey}" - " ${detailVal}" - " `;" - "};" - "" - "/* Format cells as needed */" - "const getCellContent = (h, span) => {" - " if (h in details) return detailCell(h, span);" - " else if (h in units) return `${span[h]} ${units[h]}`;" - " else if (span[h] === '') return emptyContent();" - " else if (!isDate(h)) return span[h];" - " return dateStr(span[h]);" - "};" - "" - "/* Create cell based on what header we want to render */" - "const getCell = (h, span) => (isArrayCol(h)) ? getArrayCells(h, span)" - " : `` + `${getCellContent(h, span)}`;" - "" - "/* Returns an HTML string with for a span's aggregated data" - " while columns are ordered according to its table group */" - "const tableRow = (group, span) => ''" - " + getHeadings(group).map(h => getCell(h, span)).join('')" - " + '';" - "" - "/* Returns an HTML string from all the data given as" - " table rows, with each row being a group of spans by name */" - "const tableRows = (group, data) => data.map(span => tableRow(group, span)).join('');" - "" - "/* Overwrite a table on the DOM based on the group given by adding" - " its headers and fetching data for its url */" - "function overwriteTable(group, url_end = '') {" - " fetch(getURL(group) + url_end).then(res => res.json())" - " .then(data => {" - " document.getElementById(getHTML(group))" - " .innerHTML = tableHeader(group)" - " + tableRows(group, data);" - " document.getElementById(getHTML(group) + '_count')" - " .innerHTML = data.length;" - " })" - " .catch(err => console.log(err));" - "};" - "" - "/* Adds a title subheading where needed */" - "function updateSubheading(group, name) {" - " if (hasSubheading(group)) {" - " document.getElementById(getHTML(isLatency(group) ? 'latency' : group) + '_header')" - " .innerHTML = `

${name}" - " ${(isLatency(group) ? `${latencies[group]} Bucket` : toTitlecase(group))}" - " Spans

Showing span details for up to 5 most recent spans. " - " ${getStatusHTML(group)}

`;" - " }" - "};" - "" - "/* Overwrites a table on the DOM based on the group and also" - " changes the subheader, since this a looking at sampled spans */" - "function overwriteDetailedTable(group, name) {" - " if (isLatency(group)) overwriteTable('latency', group + '/' + name);" - " else overwriteTable(group, name);" - " updateSubheading(group, name);" - "};" - "" - "/* Append to a table on the DOM based on the group given */" - "function addToTable(group, url_end = '') {" - " fetch(getURL(group) + url_end).then(res => res.json())" - " .then(data => {" - " const rowsStr = tableRows(group, data);" - " if (!rowsStr) console.log(`No rows added for ${group} table`);" - " document.getElementById(getHTML(group))" - " .getElementsByTagName('tbody')[0]" - " .innerHTML += rowsStr;" - " })" - " .catch(err => console.log(err));" - "};" - "" - "const refreshData = () => {" - " updateLastRefreshStr();" - " overwriteTable('all');" - "};" - ""; diff --git a/ext/include/opentelemetry/ext/zpages/static/tracez_style.h b/ext/include/opentelemetry/ext/zpages/static/tracez_style.h deleted file mode 100644 index 16b83f897e..0000000000 --- a/ext/include/opentelemetry/ext/zpages/static/tracez_style.h +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -const char tracez_style[] = - "" - "body {" - " color: #252525;" - " text-align: center;" - " font-family: monospace, sans-serif;" - " word-break: break-all;" - " font-size: .9em" - "}" - "" - "code {" - " font-size: 12px;" - "}" - "" - "h1 {" - " margin: 20px 0 0;" - "}" - "" - "table {" - " font-family: monospace, sans-serif;" - " border-collapse: collapse;" - " font-size: 1.05em;" - " width: 100%;" - "}" - "" - ".table-wrap {" - " width: 100%;" - " min-width: 700px;" - " max-width: 2000px;" - " margin: auto;" - "}" - "" - "td, th {" - " word-break: break-word;" - " border: 1px solid #f5f5f5;" - " padding: 6px;" - " text-align: center;" - "}" - "" - "#overview_table th, #overview_table tr {" - " border-top: none;" - "}" - "" - "#headers th, #headers tr {" - " border-bottom: none;" - "}" - "" - "#top-right {" - " text-align: right;" - " position: absolute;" - " top: 10px;" - " right: 10px;" - " text-shadow: .5px .5px .25px #fff;" - "}" - "" - "#top-right button {" - " color: #f6a81c;" - " border: 2px solid #f6a81c;" - " padding: 10px;" - " margin: 10px;" - " text-transform: uppercase;" - " letter-spacing: 1px;" - " background-color: white;" - " border-radius: 10px;" - " font-weight: bold;" - "}" - "" - ".right {" - " text-align: right;" - " padding: 10px;" - "}" - "" - ":hover {" - " transition-duration: .15s;" - "}" - "" - "#top-right button:hover {" - " border-color: #4b5fab;" - " color: #4b5fab;" - " cursor: pointer;" - "}" - "" - "tr:nth-child(even) {" - " background-color: #eee;" - "}" - "" - ".click {" - " text-decoration: underline dotted #4b5fab;" - "}" - "" - "tr:hover, td:hover, .click:hover {" - " color: white;" - " background-color: #4b5fab;" - "}" - "" - "tr:hover {" - " background-color: #4b5fabcb;" - "}" - "" - "th {" - " background-color: white;" - " color: #252525;" - "}" - "" - ".click:hover {" - " cursor: pointer;" - " color: #f6a81ccc;" - "}" - "" - ".empty {" - " color: #999;" - "}" - "" - ".sm {" - " width: 7%;" - "}" - "" - ".sm-md {" - " width: 13%;" - "}" - "" - ".md {" - " width: 23%;" - "}" - "" - ".lg {" - " width: 63%;" - "}" - "" - "img {" - " width: 50%;" - " max-width: 500px;" - "}" - "" - ".subhead-name {" - " color: #4b5fab;" - "}" - "" - ".has-tooltip {" - " text-decoration: underline dotted #f6a81c;" - "}" - "" - ".has-tooltip:hover .tooltip {" - " display: block;" - "}" - "" - ".tooltip {" - " display: none;" - " position: absolute;" - "}" - "" - ".tooltip, .tooltip:hover {" - " background: #ffffffd9;" - " padding: 10px;" - " z-index: 1000;" - " color: #252525 !important;" - " border-radius: 10px;" - " margin: 3px 20px 0 0;" - "}" - ""; diff --git a/ext/include/opentelemetry/ext/zpages/threadsafe_span_data.h b/ext/include/opentelemetry/ext/zpages/threadsafe_span_data.h deleted file mode 100644 index bd1f716ad8..0000000000 --- a/ext/include/opentelemetry/ext/zpages/threadsafe_span_data.h +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include -#include -#include - -#include "opentelemetry/common/timestamp.h" -#include "opentelemetry/nostd/string_view.h" -#include "opentelemetry/sdk/trace/recordable.h" -#include "opentelemetry/sdk/trace/span_data.h" -#include "opentelemetry/trace/span.h" -#include "opentelemetry/trace/span_id.h" -#include "opentelemetry/trace/trace_id.h" - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace ext -{ -namespace zpages -{ - -/** - * This class is a threadsafe version of span data used for zpages in OT - */ -class ThreadsafeSpanData final : public opentelemetry::sdk::trace::Recordable -{ -public: - /** - * Get the trace id for this span - * @return the trace id for this span - */ - opentelemetry::trace::TraceId GetTraceId() const noexcept - { - std::lock_guard lock(mutex_); - 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 - { - std::lock_guard lock(mutex_); - 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 - { - std::lock_guard lock(mutex_); - 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 - { - std::lock_guard lock(mutex_); - return parent_span_id_; - } - - /** - * Get the name for this span - * @return the name for this span - */ - opentelemetry::nostd::string_view GetName() const noexcept - { - std::lock_guard lock(mutex_); - return name_; - } - - /** - * Get the status for this span - * @return the status for this span - */ - opentelemetry::trace::StatusCode GetStatus() const noexcept - { - std::lock_guard lock(mutex_); - 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 - { - std::lock_guard lock(mutex_); - return status_desc_; - } - - /** - * Get the start time for this span - * @return the start time for this span - */ - opentelemetry::common::SystemTimestamp GetStartTime() const noexcept - { - std::lock_guard lock(mutex_); - return start_time_; - } - - /** - * Get the duration for this span - * @return the duration for this span - */ - std::chrono::nanoseconds GetDuration() const noexcept - { - std::lock_guard lock(mutex_); - return duration_; - } - - /** - * Get the attributes for this span - * @return the attributes for this span - */ - std::unordered_map GetAttributes() - const noexcept - { - std::lock_guard lock(mutex_); - return attributes_; - } - - void SetIdentity(const opentelemetry::trace::SpanContext &span_context, - opentelemetry::trace::SpanId parent_span_id) noexcept override - { - std::lock_guard lock(mutex_); - span_context_ = span_context; - parent_span_id_ = parent_span_id; - } - - void SetAttribute(nostd::string_view key, const common::AttributeValue &value) noexcept override - { - std::lock_guard lock(mutex_); - attributes_[std::string(key)] = nostd::visit(converter_, value); - } - - void SetStatus(opentelemetry::trace::StatusCode code, - nostd::string_view description) noexcept override - { - std::lock_guard lock(mutex_); - status_code_ = code; - status_desc_ = std::string(description); - } - - void SetName(nostd::string_view name) noexcept override - { - std::lock_guard lock(mutex_); - name_ = std::string(name); - } - - void SetSpanKind(opentelemetry::trace::SpanKind span_kind) noexcept override - { - span_kind_ = span_kind; - } - - void SetResource(const opentelemetry::sdk::resource::Resource & /*resource*/) noexcept override - { - // Not Implemented - } - - void SetStartTime(opentelemetry::common::SystemTimestamp start_time) noexcept override - { - std::lock_guard lock(mutex_); - start_time_ = start_time; - } - - void SetDuration(std::chrono::nanoseconds duration) noexcept override - { - std::lock_guard lock(mutex_); - duration_ = duration; - } - - void SetInstrumentationScope(const opentelemetry::sdk::instrumentationscope::InstrumentationScope - &instrumentation_scope) noexcept override - { - std::lock_guard lock(mutex_); - instrumentation_scope_ = &instrumentation_scope; - } - - void AddLink(const opentelemetry::trace::SpanContext &span_context, - const opentelemetry::common::KeyValueIterable &attributes = - opentelemetry::common::KeyValueIterableView>( - {})) noexcept override - { - std::lock_guard lock(mutex_); - (void)span_context; - (void)attributes; - } - - void AddEvent( - nostd::string_view name, - common::SystemTimestamp timestamp = common::SystemTimestamp(std::chrono::system_clock::now()), - const opentelemetry::common::KeyValueIterable &attributes = - opentelemetry::common::KeyValueIterableView>( - {})) noexcept override - { - std::lock_guard lock(mutex_); - events_.push_back( - opentelemetry::sdk::trace::SpanDataEvent(std::string(name), timestamp, attributes)); - } - - ThreadsafeSpanData() {} - ThreadsafeSpanData(const ThreadsafeSpanData &threadsafe_span_data) - : ThreadsafeSpanData(threadsafe_span_data, - std::lock_guard(threadsafe_span_data.mutex_)) - {} - -private: - ThreadsafeSpanData(const ThreadsafeSpanData &threadsafe_span_data, - const std::lock_guard &) - : span_context_(threadsafe_span_data.span_context_), - parent_span_id_(threadsafe_span_data.parent_span_id_), - start_time_(threadsafe_span_data.start_time_), - duration_(threadsafe_span_data.duration_), - name_(threadsafe_span_data.name_), - status_code_(threadsafe_span_data.status_code_), - status_desc_(threadsafe_span_data.status_desc_), - attributes_(threadsafe_span_data.attributes_), - events_(threadsafe_span_data.events_), - converter_(threadsafe_span_data.converter_), - instrumentation_scope_(threadsafe_span_data.instrumentation_scope_) - {} - - mutable std::mutex mutex_; - opentelemetry::trace::SpanContext span_context_{false, false}; - opentelemetry::trace::SpanId parent_span_id_; - common::SystemTimestamp start_time_; - std::chrono::nanoseconds duration_{0}; - std::string name_; - opentelemetry::trace::SpanKind span_kind_; - opentelemetry::trace::StatusCode status_code_{opentelemetry::trace::StatusCode::kUnset}; - std::string status_desc_; - std::unordered_map attributes_; - std::vector events_; - opentelemetry::sdk::common::AttributeConverter converter_; - const opentelemetry::sdk::instrumentationscope::InstrumentationScope *instrumentation_scope_; -}; -} // namespace zpages -} // namespace ext -OPENTELEMETRY_END_NAMESPACE diff --git a/ext/include/opentelemetry/ext/zpages/tracez_data.h b/ext/include/opentelemetry/ext/zpages/tracez_data.h deleted file mode 100644 index 5f36a6b047..0000000000 --- a/ext/include/opentelemetry/ext/zpages/tracez_data.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include -#include -#include - -#include "opentelemetry/ext/zpages/threadsafe_span_data.h" -#include "opentelemetry/nostd/span.h" -#include "opentelemetry/nostd/string_view.h" -#include "opentelemetry/sdk/trace/span_data.h" -#include "opentelemetry/trace/span_id.h" -#include "opentelemetry/trace/trace_id.h" -#include "opentelemetry/version.h" - -using opentelemetry::ext::zpages::ThreadsafeSpanData; -using opentelemetry::trace::SpanId; -using opentelemetry::trace::TraceId; - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace ext -{ -namespace zpages -{ - -/** - * kMaxNumberOfSampleSpans is the maximum number of running, completed or error - * sample spans stored at any given time for a given span name. - * This limit is introduced to reduce memory usage by trimming sample spans - * stored. - */ -const int kMaxNumberOfSampleSpans = 5; - -/** - * TracezData is the data to be displayed for tracez zpages that is stored for - * each span name. - */ -struct TracezData -{ - /** - * TODO: At this time the maximum count is unknown but a larger data type - * might have to be used in the future to store these counts to avoid overflow - */ - unsigned int running_span_count; - unsigned int error_span_count; - - /** - * completed_span_count_per_latency_bucket is an array that stores the count - * of spans for each of the 9 latency buckets. - */ - std::array completed_span_count_per_latency_bucket; - - /** - * sample_latency_spans is an array of lists, each index of the array - * corresponds to a latency boundary(of which there are 9). - * The list in each index stores the sample spans for that latency boundary. - */ - std::array, kLatencyBoundaries.size()> sample_latency_spans; - - /** - * sample_error_spans is a list that stores the error samples for a span name. - */ - std::list sample_error_spans; - - /** - * sample_running_spans is a list that stores the running span samples for a - * span name. - */ - std::list sample_running_spans; - - TracezData() - { - running_span_count = 0; - error_span_count = 0; - completed_span_count_per_latency_bucket.fill(0); - } -}; - -} // namespace zpages -} // namespace ext -OPENTELEMETRY_END_NAMESPACE diff --git a/ext/include/opentelemetry/ext/zpages/tracez_data_aggregator.h b/ext/include/opentelemetry/ext/zpages/tracez_data_aggregator.h deleted file mode 100644 index 5bc7e847db..0000000000 --- a/ext/include/opentelemetry/ext/zpages/tracez_data_aggregator.h +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "opentelemetry/ext/zpages/latency_boundaries.h" -#include "opentelemetry/ext/zpages/tracez_data.h" -#include "opentelemetry/ext/zpages/tracez_shared_data.h" -#include "opentelemetry/nostd/span.h" -#include "opentelemetry/nostd/string_view.h" -#include "opentelemetry/sdk/trace/span_data.h" - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace ext -{ -namespace zpages -{ -/** - * TracezDataAggregator object is responsible for collecting raw data and - * converting it to useful information that can be made available to - * display on the tracez zpage. - * - * When this object is created it starts a thread that calls a function - * periodically to update the aggregated data with new spans. - * - * The only exposed function is a getter that returns a copy of the aggregated - * data when requested. This function is ensured to be called in sequence to the - * aggregate spans function which is called periodically. - * - * TODO: Consider a singleton pattern for this class, not sure if multiple - * instances of this class should exist. - */ -class TracezDataAggregator -{ -public: - /** - * Constructor creates a thread that calls a function to aggregate span data - * at regular intervals. - * @param shared_data is the shared set of spans to expose. - * @param update_interval the time duration for updating the aggregated data. - */ - TracezDataAggregator(std::shared_ptr shared_data, - milliseconds update_interval = milliseconds(10)); - - /** Ends the thread set up in the constructor and destroys the object **/ - ~TracezDataAggregator(); - - /** - * GetAggregatedTracezData returns a copy of the updated data. - * @returns a map with the span name as key and the tracez span data as value. - */ - std::map GetAggregatedTracezData(); - -private: - /** - * AggregateSpans is the function that is called to update the aggregated data - * with newly completed and running span data - */ - void AggregateSpans(); - - /** - * AggregateCompletedSpans is the function that is called to update the - * aggregation with the data of newly completed spans. - * @param completed_spans are the newly completed spans. - */ - void AggregateCompletedSpans(std::vector> &completed_spans); - - /** - * AggregateRunningSpans aggregates the data for all running spans received - * from the span processor. Running spans are not cleared by the span - * processor and multiple calls to this function may contain running spans for - * which data has already been collected in a previous call. Additionally, - * span names can change while span is running and there seems to be - * no trivial to way to know if it is a new or old running span so at every - * call to this function the available running span data is reset and - * recalculated. At this time there is no unique way to identify a span - * object once this is done, there might be some better ways to do this. - * TODO : SpanProcessor is never notified when a span name is changed while it - * is running and that is propogated to the data aggregator. The running span - * name if changed while it is running will not be updated in the data - * aggregator till the span is completed. - * @param running_spans is the running spans to be aggregated. - */ - void AggregateRunningSpans(std::unordered_set &running_spans); - - /** - * AggregateStatusOKSpans is the function called to update the data of spans - * with status code OK. - * @param ok_span is the span who's data is to be aggregated - */ - void AggregateStatusOKSpan(std::unique_ptr &ok_span); - - /** - * AggregateStatusErrorSpans is the function that is called to update the - * data of error spans - * @param error_span is the error span who's data is to be aggregated - */ - void AggregateStatusErrorSpan(std::unique_ptr &error_span); - - /** - * ClearRunningSpanData is a function that is used to clear all running span - * at the beginning of a call to AggregateSpan data. - * Running span data has to be cleared before aggregation because running - * span data is recalculated at every call to AggregateSpans. - */ - void ClearRunningSpanData(); - - /** - * FindLatencyBoundary finds the latency boundary to which the duration of - * the given span_data belongs to - * @ param span_data is the ThreadsafeSpanData whose duration for which the latency - * boundary is to be found - * @ returns LatencyBoundary is the latency boundary that the duration belongs - * to - */ - LatencyBoundary FindLatencyBoundary(std::unique_ptr &ok_span); - - /** - * InsertIntoSampleSpanList is a helper function that is called to insert - * a given span into a sample span list. A function is used for insertion - * because list size is to be limited at a set maximum. - * @param sample_spans the sample span list into which span is to be inserted - * @param span_data the span_data to be inserted into list - */ - void InsertIntoSampleSpanList(std::list &sample_spans, - ThreadsafeSpanData &span_data); - - /** Instance of shared spans used to collect raw data **/ - std::shared_ptr tracez_shared_data_; - - /** - * Tree map with key being the name of the span and value being a unique ptr - * that stores the tracez span data for the given span name - * A tree map is preferred to a hash map because the the data is to be ordered - * in alphabetical order of span name. - * TODO : A possible memory concern if there are too many unique - * span names, one solution could be to implement a LRU cache that trims the - * DS based on frequency of usage of a span name. - */ - std::map aggregated_tracez_data_; - std::mutex mtx_; - - /** A boolean that is set to true in the constructor and false in the - * destructor to start and end execution of aggregate spans **/ - std::atomic execute_{false}; - - /** Thread that executes aggregate spans at regurlar intervals during this - object's lifetime**/ - std::thread aggregate_spans_thread_; - - /** Condition variable that notifies the thread when object is about to be - destroyed **/ - std::condition_variable cv_; -}; - -} // namespace zpages -} // namespace ext -OPENTELEMETRY_END_NAMESPACE diff --git a/ext/include/opentelemetry/ext/zpages/tracez_http_server.h b/ext/include/opentelemetry/ext/zpages/tracez_http_server.h deleted file mode 100644 index eaa3fac8d3..0000000000 --- a/ext/include/opentelemetry/ext/zpages/tracez_http_server.h +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "nlohmann/json.hpp" -#include "opentelemetry/ext/zpages/static/tracez_index.h" -#include "opentelemetry/ext/zpages/static/tracez_script.h" -#include "opentelemetry/ext/zpages/static/tracez_style.h" -#include "opentelemetry/ext/zpages/tracez_data_aggregator.h" -#include "opentelemetry/ext/zpages/zpages_http_server.h" - -#define HAVE_HTTP_DEBUG -#define HAVE_CONSOLE_LOG - -using json = nlohmann::json; - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace ext -{ -namespace zpages -{ - -class TracezHttpServer : public opentelemetry::ext::zpages::zPagesHttpServer -{ -public: - /** - * Construct the server by initializing the endpoint for querying TraceZ aggregation data and - * files, along with taking ownership of the aggregator whose data is used to send data to the - * frontend - * @param aggregator is the TraceZ Data Aggregator, which calculates aggregation info - * @param host is the host where the TraceZ webpages will be displayed, default being localhost - * @param port is the port where the TraceZ webpages will be displayed, default being 30000 - */ - TracezHttpServer(std::unique_ptr &&aggregator, - const std::string &host = "localhost", - int port = 30000) - : opentelemetry::ext::zpages::zPagesHttpServer("/tracez", host, port), - data_aggregator_(std::move(aggregator)) - { - InitializeTracezEndpoint(*this); - }; - -private: - /** - * Set the HTTP server to use the "Serve" callback to send the appropriate data when queried - * @param server, which should be an instance of this object - */ - void InitializeTracezEndpoint(TracezHttpServer &server) { server[endpoint_] = Serve; } - - /** - * Updates the stored aggregation data (aggregations_) using the data aggregator - */ - void UpdateAggregations(); - - /** - * First updates the stored aggregations, then translates that data from a C++ map to - * a JSON object - * @returns JSON object of collected spans bucket counts by name - */ - json GetAggregations(); - - /** - * Using the stored aggregations, finds the span group with the right name and returns - * its running span data as a JSON, only grabbing the fields needed for the frontend - * @param name of the span group whose running data we want - * @returns JSON representing running span data with the passed in name - */ - json GetRunningSpansJSON(const std::string &name); - - /** - * Using the stored aggregations, finds the span group with the right name and returns - * its error span data as a JSON, only grabbing the fields needed for the frontend - * @param name of the span group whose running data we want - * @returns JSON representing eoor span data with the passed in name - */ - json GetErrorSpansJSON(const std::string &name); - - /** - * Using the stored aggregations, finds the span group with the right name and bucket index - * returning its latency span data as a JSON, only grabbing the fields needed for the frontend - * @param name of the span group whose latency data we want - * @param index of which latency bucket to grab from - * @returns JSON representing bucket span data with the passed in name and latency range - */ - json GetLatencySpansJSON(const std::string &name, int latency_range_index); - - /** - * Returns attributes, which have varied types, from a span data to convert into JSON - * @param sample current span data, whose attributes we want to extract - * @returns JSON representing attributes for a given threadsafe span data - */ - json GetAttributesJSON(const opentelemetry::ext::zpages::ThreadsafeSpanData &sample); - - /** - * Sets the response object with the TraceZ aggregation data based on the request endpoint - * @param req is the HTTP request, which we use to figure out the response to send - * @param resp is the HTTP response we want to send to the frontend, either webpage or TraceZ - * aggregation data - */ - HTTP_SERVER_NS::HttpRequestCallback Serve{ - [&](HTTP_SERVER_NS::HttpRequest const &req, HTTP_SERVER_NS::HttpResponse &resp) { - std::string query = GetQuery(req.uri); // tracez - - if (StartsWith(query, "get")) - { - resp.headers[HTTP_SERVER_NS::CONTENT_TYPE] = "application/json"; - query = GetAfterSlash(query); - if (StartsWith(query, "latency")) - { - auto queried_latency_name = GetAfterSlash(query); - auto queried_latency_index = std::stoi(GetBeforeSlash(queried_latency_name)); - auto queried_name = GetAfterSlash(queried_latency_name); - ReplaceHtmlChars(queried_name); - resp.body = GetLatencySpansJSON(queried_name, queried_latency_index).dump(); - } - else - { - auto queried_name = GetAfterSlash(query); - ReplaceHtmlChars(queried_name); - if (StartsWith(query, "aggregations")) - { - resp.body = GetAggregations().dump(); - } - else if (StartsWith(query, "running")) - { - resp.body = GetRunningSpansJSON(queried_name).dump(); - } - else if (StartsWith(query, "error")) - { - resp.body = GetErrorSpansJSON(queried_name).dump(); - } - else - { - resp.body = json::array().dump(); - } - } - } - else - { - if (StartsWith(query, "script.js")) - { - resp.headers[HTTP_SERVER_NS::CONTENT_TYPE] = "text/javascript"; - resp.body = tracez_script; - } - else if (StartsWith(query, "style.css")) - { - resp.headers[HTTP_SERVER_NS::CONTENT_TYPE] = "text/css"; - resp.body = tracez_style; - } - else if (query.empty() || query == "/tracez" || StartsWith(query, "index.html")) - { - resp.headers[HTTP_SERVER_NS::CONTENT_TYPE] = "text/html"; - resp.body = tracez_index; - } - else - { - resp.headers[HTTP_SERVER_NS::CONTENT_TYPE] = "text/plain"; - resp.body = "Invalid query: " + query; - } - } - - return 200; - }}; - - std::map aggregated_data_; - std::unique_ptr data_aggregator_; -}; - -} // namespace zpages -} // namespace ext -OPENTELEMETRY_END_NAMESPACE diff --git a/ext/include/opentelemetry/ext/zpages/tracez_processor.h b/ext/include/opentelemetry/ext/zpages/tracez_processor.h deleted file mode 100644 index 081dd41117..0000000000 --- a/ext/include/opentelemetry/ext/zpages/tracez_processor.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "opentelemetry/ext/zpages/threadsafe_span_data.h" -#include "opentelemetry/ext/zpages/tracez_shared_data.h" -#include "opentelemetry/sdk/trace/processor.h" -#include "opentelemetry/sdk/trace/recordable.h" - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace ext -{ -namespace zpages -{ -/* - * The span processor passes and stores running and completed recordables (casted as span_data) - * to be used by the TraceZ Data Aggregator. - */ -class TracezSpanProcessor : public opentelemetry::sdk::trace::SpanProcessor -{ -public: - /* - * Initialize a span processor. - */ - explicit TracezSpanProcessor(std::shared_ptr shared_data) noexcept - : shared_data_(shared_data) - {} - - /* - * Create a span recordable, which is span_data - * @return a newly initialized recordable - */ - std::unique_ptr MakeRecordable() noexcept override - { - return std::unique_ptr(new ThreadsafeSpanData); - } - - /* - * OnStart is called when a span starts; the recordable is cast to span_data and added to - * running_spans. - * @param span a recordable for a span that was just started - */ - void OnStart(opentelemetry::sdk::trace::Recordable &span, - const opentelemetry::trace::SpanContext &parent_context) noexcept override; - - /* - * OnEnd is called when a span ends; that span_data is moved from running_spans to - * completed_spans - * @param span a recordable for a span that was ended - */ - void OnEnd(std::unique_ptr &&span) noexcept override; - - /* - * For now, does nothing. In the future, it - * may send all ended spans that have not yet been sent to the aggregator. - * @param timeout an optional timeout. Currently, timeout does nothing. - * @return return the status of the operation. - */ - bool ForceFlush(std::chrono::microseconds timeout OPENTELEMETRY_MAYBE_UNUSED = - std::chrono::microseconds::max()) noexcept override - { - return true; - } - - /* - * Shut down the processor and do any cleanup required, which is none. - * 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. Currently, timeout does nothing. - * @return return the status of the operation. - */ - bool Shutdown(std::chrono::microseconds timeout OPENTELEMETRY_MAYBE_UNUSED = - std::chrono::microseconds::max()) noexcept override - { - return true; - } - -private: - std::shared_ptr shared_data_; -}; -} // namespace zpages -} // namespace ext -OPENTELEMETRY_END_NAMESPACE diff --git a/ext/include/opentelemetry/ext/zpages/tracez_shared_data.h b/ext/include/opentelemetry/ext/zpages/tracez_shared_data.h deleted file mode 100644 index 571661550f..0000000000 --- a/ext/include/opentelemetry/ext/zpages/tracez_shared_data.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "opentelemetry/ext/zpages/threadsafe_span_data.h" -#include "opentelemetry/sdk/trace/processor.h" -#include "opentelemetry/sdk/trace/recordable.h" - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace ext -{ -namespace zpages -{ -/* - * The span processor passes and stores running and completed recordables (casted as span_data) - * to be used by the TraceZ Data Aggregator. - */ -class TracezSharedData -{ -public: - struct CollectedSpans - { - std::unordered_set running; - std::vector> completed; - }; - - /* - * Initialize a shared data storage. - */ - explicit TracezSharedData() noexcept {} - - /* - * Called when a span has been started. - */ - void OnStart(ThreadsafeSpanData *span) noexcept; - - /* - * Called when a span has ended. - */ - void OnEnd(std::unique_ptr &&span) noexcept; - - /* - * Returns a snapshot of all spans stored. This snapshot has a copy of the - * stored running_spans and gives ownership of completed spans to the caller. - * Stored completed_spans are cleared from the processor. Currently, - * copy-on-write is utilized where possible to minimize contention, but locks - * may be added in the future. - * @return snapshot of all currently running spans and newly completed spans - * (spans never sent while complete) at the time that the function is called - */ - CollectedSpans GetSpanSnapshot() noexcept; - -private: - mutable std::mutex mtx_; - CollectedSpans spans_; -}; -} // namespace zpages -} // namespace ext -OPENTELEMETRY_END_NAMESPACE diff --git a/ext/include/opentelemetry/ext/zpages/zpages.h b/ext/include/opentelemetry/ext/zpages/zpages.h deleted file mode 100644 index f0242d92d7..0000000000 --- a/ext/include/opentelemetry/ext/zpages/zpages.h +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include - -#include "opentelemetry/ext/zpages/tracez_data_aggregator.h" -#include "opentelemetry/ext/zpages/tracez_http_server.h" -#include "opentelemetry/ext/zpages/tracez_processor.h" -#include "opentelemetry/ext/zpages/tracez_shared_data.h" - -#include "opentelemetry/nostd/shared_ptr.h" -#include "opentelemetry/sdk/trace/tracer_provider.h" -#include "opentelemetry/trace/provider.h" - -using opentelemetry::ext::zpages::TracezDataAggregator; -using opentelemetry::ext::zpages::TracezHttpServer; -using opentelemetry::ext::zpages::TracezSharedData; -using opentelemetry::ext::zpages::TracezSpanProcessor; -using std::chrono::microseconds; - -/** - * Wrapper for zPages that initializes all the components required for zPages, - * and starts the HTTP server in the constructor and ends it in the destructor. - * The constructor and destructor for this object is private to prevent - * creation other than by calling the static function Initialize(). This follows the - * meyers singleton pattern and only a single instance of the class is allowed. - */ -class ZPages -{ -public: - /** - * This function is called if the user wishes to include zPages in their - * application. It creates a static instance of this class and replaces the - * global TracerProvider with one that delegates spans to tracez. - */ - static void Initialize() { Instance().ReplaceGlobalProvider(); } - - /** - * Returns the singletone instnace of ZPages, useful for attaching z-pages span processors to - * non-global providers. - * - * Note: This will instantiate the Tracez instance and webserver if it hasn't already been - * instantiated. - */ - static ZPages &Instance() - { - static ZPages instance; - return instance; - } - - /** Replaces the global tracer provider with an instance that exports to tracez. */ - void ReplaceGlobalProvider() - { - // GCC 4.8 can't infer the type coercion. - std::unique_ptr processor( - MakeSpanProcessor().release()); - auto tracez_provider_ = opentelemetry::nostd::shared_ptr( - new opentelemetry::sdk::trace::TracerProvider(std::move(processor))); - opentelemetry::trace::Provider::SetTracerProvider(tracez_provider_); - } - - /** Retruns a new span processor that will output to z-pages. */ - std::unique_ptr MakeSpanProcessor() - { - return std::unique_ptr(new TracezSpanProcessor(tracez_shared_)); - } - -private: - /** - * Constructor is responsible for initializing the tracer, tracez processor, - * tracez data aggregator and the tracez server. The server is also started in - * constructor. - */ - ZPages() - { - // Construct shared data nd start tracez webserver. - tracez_shared_ = std::make_shared(); - auto tracez_aggregator = - std::unique_ptr(new TracezDataAggregator(tracez_shared_)); - tracez_server_ = - std::unique_ptr(new TracezHttpServer(std::move(tracez_aggregator))); - tracez_server_->start(); - } - - ~ZPages() - { - // shut down the server when the object goes out of scope(at the end of the - // program) - tracez_server_->stop(); - } - std::shared_ptr tracez_shared_; - std::unique_ptr tracez_server_; -}; diff --git a/ext/include/opentelemetry/ext/zpages/zpages_http_server.h b/ext/include/opentelemetry/ext/zpages/zpages_http_server.h deleted file mode 100644 index abf6c1a5b4..0000000000 --- a/ext/include/opentelemetry/ext/zpages/zpages_http_server.h +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "opentelemetry/ext/http/server/http_server.h" - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace ext -{ -namespace zpages -{ - -class zPagesHttpServer : public HTTP_SERVER_NS::HttpServer -{ -protected: - /** - * Construct the server by initializing the endpoint for serving static files, which show up on - * the web if the user is on the given host:port. Static files can be seen relative to the folder - * where the executable was ran. - * @param host is the host where the TraceZ webpages will be displayed - * @param port is the port where the TraceZ webpages will be displayed - * @param endpoint is where this specific zPage will server files - */ - zPagesHttpServer(const std::string &endpoint, - const std::string &host = "127.0.0.1", - int port = 52620) - : HttpServer(), endpoint_(endpoint) - { - std::ostringstream os; - os << host << ":" << port; - setServerName(os.str()); - addListeningPort(port); - }; - - /** - * Helper function that returns query information by isolating it from the base endpoint - * @param uri is the full query - */ - std::string GetQuery(const std::string &uri) - { - if (endpoint_.length() + 1 > uri.length()) - return uri; - return uri.substr(endpoint_.length() + 1); - } - - /** - * Helper that returns whether a str starts with pre - * @param str is the string we're checking - * @param pre is the prefix we're checking against - */ - bool StartsWith(const std::string &str, const std::string &pre) { return str.rfind(pre, 0) == 0; } - - /** - * Helper that returns the remaining string after the leftmost backslash - * @param str is the string we're extracting from - */ - std::string GetAfterSlash(const std::string &str) - { - std::size_t backslash = str.find("/"); - if (backslash == std::string::npos || backslash == str.length()) - return ""; - return str.substr(backslash + 1); - } - - /** - * Helper that returns the remaining string after the leftmost backslash - * @param str is the string we're extracting from - */ - std::string GetBeforeSlash(const std::string &str) - { - std::size_t backslash = str.find("/"); - if (backslash == std::string::npos || backslash == str.length()) - return str; - return str.substr(0, backslash); - } - - /** - * Helper that replaces all occurrences a string within a string - * @param str string to modify - * @param search substring to remove from str - * @param replacement string to replace search with whenever search is found - */ - void ReplaceAll(std::string &str, const std::string &search, const std::string &replacement) - { - size_t idx = str.find(search, 0); - while (idx != std::string::npos) - { - str.replace(idx, search.length(), replacement); - idx = str.find(search, idx); - } - } - - /** - * Helper that replaces all special HTML/address base encoded characters - * into what they're originally supposed to be - * @param str string to conduct replacements for - */ - void ReplaceHtmlChars(std::string &str) - { - for (const auto &replace_pair : replace_map_) - { - ReplaceAll(str, replace_pair.first, replace_pair.second); - } - } - - const std::string endpoint_; - const std::unordered_map replace_map_ = {{"%20", " "}}; -}; - -} // namespace zpages -} // namespace ext -OPENTELEMETRY_END_NAMESPACE diff --git a/ext/src/CMakeLists.txt b/ext/src/CMakeLists.txt index bc1f614eb9..65aaa307c0 100644 --- a/ext/src/CMakeLists.txt +++ b/ext/src/CMakeLists.txt @@ -1,10 +1,6 @@ # Copyright The OpenTelemetry Authors # SPDX-License-Identifier: Apache-2.0 -if(WITH_ZPAGES) - add_subdirectory(zpages) -endif() - if(WITH_HTTP_CLIENT_CURL) add_subdirectory(http/client/curl) endif() diff --git a/ext/src/zpages/BUILD b/ext/src/zpages/BUILD deleted file mode 100644 index ce23147df5..0000000000 --- a/ext/src/zpages/BUILD +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright The OpenTelemetry Authors -# SPDX-License-Identifier: Apache-2.0 - -package(default_visibility = ["//visibility:public"]) - -cc_library( - name = "zpages", - srcs = glob(["**/*.cc"]), - hdrs = glob(["**/*.h"]), - include_prefix = "ext/zpages", - deps = [ - "//api", - "//ext:headers", - "//sdk:headers", - "@github_nlohmann_json//:json", - ], -) diff --git a/ext/src/zpages/CMakeLists.txt b/ext/src/zpages/CMakeLists.txt deleted file mode 100644 index 8ff225112b..0000000000 --- a/ext/src/zpages/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright The OpenTelemetry Authors -# SPDX-License-Identifier: Apache-2.0 - -add_library( - opentelemetry_zpages - tracez_processor.cc - tracez_shared_data.cc - tracez_data_aggregator.cc - ../../include/opentelemetry/ext/zpages/tracez_shared_data.h - ../../include/opentelemetry/ext/zpages/tracez_processor.h - ../../include/opentelemetry/ext/zpages/tracez_data_aggregator.h - ../../include/opentelemetry/ext/zpages/tracez_http_server.h) - -set_target_properties(opentelemetry_zpages PROPERTIES EXPORT_NAME zpages) -set_target_version(opentelemetry_zpages) - -target_link_libraries(opentelemetry_zpages PUBLIC opentelemetry_ext - opentelemetry_trace) - -if(OPENTELEMETRY_INSTALL) - install( - TARGETS opentelemetry_zpages - EXPORT "${PROJECT_NAME}-target" - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) -endif() diff --git a/ext/src/zpages/README.md b/ext/src/zpages/README.md deleted file mode 100644 index f182fd43f3..0000000000 --- a/ext/src/zpages/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# zPages - -## Overview - -zPages are a quick and light way to view tracing and metrics information on -standard OpenTelemetry C++ instrumented applications. It requires no external -dependencies or backend setup. See more information in the OTel zPages -experimental -[spec](https://github.com/open-telemetry/opentelemetry-specification/blob/5b86d4b6c42e6d1e47d9155ac1e2e27f0f0b7769/experimental/trace/zpages.md). -OTel C++ currently only offers Tracez; future zPages to potentially add include -TraceConfigz, RPCz, and Statsz. Events and links need to be added to Tracez. - -## Usage - -> TODO: Add CMake instructions - -1: Add the following 2 lines of code - -* `#include opentelemetry/ext/zpages/zpages.h // include zPages` -* `zpages::Initialize; // start up zPages in your app, before any tracing/span - code` - -2: Build and run your application normally - -For example, you can do this for the zPages example while at the root -`opentelemetry-cpp` directory with: - -```sh -bazel build //examples/zpages:zpages_example -bazel-bin/examples/zpages/zpages_example -``` - -If you look at the [zPages example's source -code](https://github.com/open-telemetry/opentelemetry-cpp/blob/main/examples/zpages/zpages_example.cc), -it demonstrates adding zPages, manual application instrumentation (which sends -data to zPages for viewing), and simulated use cases for zPages. - -3: View zPages at `http://localhost:3000/tracez` - -## More Information - -* OTel zPages experimental - [spec](https://github.com/open-telemetry/opentelemetry-specification/blob/5b86d4b6c42e6d1e47d9155ac1e2e27f0f0b7769/experimental/trace/zpages.md) -* [zPages General Direction Spec - (OTEP)](https://github.com/open-telemetry/oteps/blob/main/text/0110-z-pages.md) -* OTel C++ Design Docs - * [Tracez Span - Processor](https://docs.google.com/document/d/1kO4iZARYyr-EGBlY2VNM3ELU3iw6ZrC58Omup_YT-fU/edit#) - * [Tracez Data - Aggregator](https://docs.google.com/document/d/1ziKFgvhXFfRXZjOlAHQRR-TzcNcTXzg1p2I9oPCEIoU/edit?ts=5ef0d177#heading=h.5irk4csrpu0y) - * [Tracez Http - Server](https://docs.google.com/document/d/1U1V8QZ5LtGl4Mich-aJ6KZGLHrMIE8pWyspmzvnIefI/edit#) - * includes reference pictures of the zPages/Tracez UI diff --git a/ext/src/zpages/tracez_data_aggregator.cc b/ext/src/zpages/tracez_data_aggregator.cc deleted file mode 100644 index 993fc182ce..0000000000 --- a/ext/src/zpages/tracez_data_aggregator.cc +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#include "opentelemetry/ext/zpages/tracez_data_aggregator.h" - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace ext -{ -namespace zpages -{ - -TracezDataAggregator::TracezDataAggregator(std::shared_ptr shared_data, - milliseconds update_interval) -{ - tracez_shared_data_ = shared_data; - - // Start a thread that calls AggregateSpans periodically or till notified. - execute_.store(true, std::memory_order_release); - aggregate_spans_thread_ = std::thread([this, update_interval]() { - while (execute_.load(std::memory_order_acquire)) - { - std::unique_lock lock(mtx_); - AggregateSpans(); - cv_.wait_for(lock, update_interval); - } - }); -} - -TracezDataAggregator::~TracezDataAggregator() -{ - // Notify and join the thread so object can be destroyed without wait for wake - if (execute_.load(std::memory_order_acquire)) - { - execute_.store(false, std::memory_order_release); - cv_.notify_one(); - aggregate_spans_thread_.join(); - } -} - -std::map TracezDataAggregator::GetAggregatedTracezData() -{ - std::unique_lock lock(mtx_); - return aggregated_tracez_data_; -} - -LatencyBoundary TracezDataAggregator::FindLatencyBoundary( - std::unique_ptr &span_data) -{ - const auto &span_data_duration = span_data->GetDuration(); - for (unsigned int boundary = 0; boundary < kLatencyBoundaries.size() - 1; boundary++) - { - if (span_data_duration < kLatencyBoundaries[boundary + 1]) - return (LatencyBoundary)boundary; - } - return LatencyBoundary::k100SecondToMax; -} - -void TracezDataAggregator::InsertIntoSampleSpanList(std::list &sample_spans, - ThreadsafeSpanData &span_data) -{ - /** - * Check to see if the sample span list size exceeds the set limit, if it does - * free up memory and remove the earliest inserted sample before appending - */ - if (sample_spans.size() == kMaxNumberOfSampleSpans) - { - sample_spans.pop_front(); - } - sample_spans.push_back(ThreadsafeSpanData(span_data)); -} - -void TracezDataAggregator::ClearRunningSpanData() -{ - auto it = aggregated_tracez_data_.begin(); - while (it != aggregated_tracez_data_.end()) - { - it->second.running_span_count = 0; - it->second.sample_running_spans.clear(); - - // Check if any data exists in the struct, if not delete entry - bool is_completed_span_count_zero = true; - for (const auto &completed_span_count : it->second.completed_span_count_per_latency_bucket) - { - if (completed_span_count > 0) - is_completed_span_count_zero = false; - } - - if (it->second.error_span_count == 0 && is_completed_span_count_zero) - { - it = aggregated_tracez_data_.erase(it); - } - else - { - ++it; - } - } -} - -void TracezDataAggregator::AggregateStatusOKSpan(std::unique_ptr &ok_span) -{ - // Find and update boundary of aggregated data that span belongs - auto boundary_name = FindLatencyBoundary(ok_span); - - // Get the data for name in aggrgation and update count and sample spans - auto &tracez_data = aggregated_tracez_data_.at(ok_span->GetName().data()); - InsertIntoSampleSpanList(tracez_data.sample_latency_spans[boundary_name], *ok_span.get()); - tracez_data.completed_span_count_per_latency_bucket[boundary_name]++; -} - -void TracezDataAggregator::AggregateStatusErrorSpan(std::unique_ptr &error_span) -{ - // Get data for name in aggregation and update count and sample spans - auto &tracez_data = aggregated_tracez_data_.at(error_span->GetName().data()); - InsertIntoSampleSpanList(tracez_data.sample_error_spans, *error_span.get()); - tracez_data.error_span_count++; -} - -void TracezDataAggregator::AggregateCompletedSpans( - std::vector> &completed_spans) -{ - for (auto &completed_span : completed_spans) - { - std::string span_name = completed_span->GetName().data(); - - if (aggregated_tracez_data_.find(span_name) == aggregated_tracez_data_.end()) - { - aggregated_tracez_data_[span_name] = TracezData(); - } - - if (completed_span->GetStatus() == trace::StatusCode::kOk || - completed_span->GetStatus() == trace::StatusCode::kUnset) - AggregateStatusOKSpan(completed_span); - else - AggregateStatusErrorSpan(completed_span); - } -} - -void TracezDataAggregator::AggregateRunningSpans( - std::unordered_set &running_spans) -{ - for (auto &running_span : running_spans) - { - std::string span_name = running_span->GetName().data(); - - if (aggregated_tracez_data_.find(span_name) == aggregated_tracez_data_.end()) - { - aggregated_tracez_data_[span_name] = TracezData(); - } - - auto &tracez_data = aggregated_tracez_data_[span_name]; - InsertIntoSampleSpanList(aggregated_tracez_data_[span_name].sample_running_spans, - *running_span); - tracez_data.running_span_count++; - } -} - -void TracezDataAggregator::AggregateSpans() -{ - auto span_snapshot = tracez_shared_data_->GetSpanSnapshot(); - /** - * TODO: At this time in the project, there is no way of uniquely identifying - * a span(their id's are not being set yet). - * If in the future this is added then clearing of running spans will not bee - * required. - * For now this step of clearing and recalculating running span data is - * required because it is unknown which spans have moved from running to - * completed since the previous call. Additionally, the span name can change - * for spans while they are running. - * - * A better approach for identifying moved spans would have been to map - * span id to span name, find these span names in the aggregated data and then - * delete only this information for running span data as opposed to clearing - * all running span data. However this cannot be done at this time because, - * unique identifiers to span data have not been added yet. - * - * A few things to note: - * i) Duplicate running spans may be received from the span processor in one - * multiple successive calls to this function. - * ii) Only the newly completed spans are received by this function. - * Completed spans will not be seen more than once - **/ - ClearRunningSpanData(); - AggregateCompletedSpans(span_snapshot.completed); - AggregateRunningSpans(span_snapshot.running); -} - -} // namespace zpages -} // namespace ext -OPENTELEMETRY_END_NAMESPACE diff --git a/ext/src/zpages/tracez_http_server.cc b/ext/src/zpages/tracez_http_server.cc deleted file mode 100644 index a5d0072787..0000000000 --- a/ext/src/zpages/tracez_http_server.cc +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#include "opentelemetry/ext/zpages/tracez_http_server.h" - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace ext -{ -namespace zpages -{ -namespace nostd = opentelemetry::nostd; - -json TracezHttpServer::GetAggregations() -{ - aggregated_data_ = data_aggregator_->GetAggregatedTracezData(); - auto counts_json = json::array(); - - for (const auto &aggregation_group : aggregated_data_) - { - const auto &buckets = aggregation_group.second; - const auto &complete_ok_counts = buckets.completed_span_count_per_latency_bucket; - - auto latency_counts = json::array(); - for (unsigned int boundary = 0; boundary < kLatencyBoundaries.size(); boundary++) - { - latency_counts.push_back(complete_ok_counts[boundary]); - } - - counts_json.push_back({{"name", aggregation_group.first}, - {"error", buckets.error_span_count}, - {"running", buckets.running_span_count}, - {"latency", latency_counts}}); - } - return counts_json; -} - -json TracezHttpServer::GetRunningSpansJSON(const std::string &name) -{ - auto running_json = json::array(); - - auto grouping = aggregated_data_.find(name); - - if (grouping != aggregated_data_.end()) - { - const auto &running_samples = grouping->second.sample_running_spans; - for (const auto &sample : running_samples) - { - running_json.push_back({ - {"spanid", std::string(reinterpret_cast(sample.GetSpanId().Id().data()))}, - {"parentid", - std::string(reinterpret_cast(sample.GetParentSpanId().Id().data()))}, - {"traceid", std::string(reinterpret_cast(sample.GetTraceId().Id().data()))}, - {"start", sample.GetStartTime().time_since_epoch().count()}, - {"attributes", GetAttributesJSON(sample)}, - }); - } - } - return running_json; -} - -json TracezHttpServer::GetErrorSpansJSON(const std::string &name) -{ - auto error_json = json::array(); - - auto grouping = aggregated_data_.find(name); - - if (grouping != aggregated_data_.end()) - { - const auto &error_samples = grouping->second.sample_error_spans; - for (const auto &sample : error_samples) - { - error_json.push_back({ - {"spanid", std::string(reinterpret_cast(sample.GetSpanId().Id().data()))}, - {"parentid", - std::string(reinterpret_cast(sample.GetParentSpanId().Id().data()))}, - {"traceid", std::string(reinterpret_cast(sample.GetTraceId().Id().data()))}, - {"start", sample.GetStartTime().time_since_epoch().count()}, - {"status", (unsigned short)sample.GetStatus()}, - {"attributes", GetAttributesJSON(sample)}, - }); - } - } - return error_json; -} - -json TracezHttpServer::GetLatencySpansJSON(const std::string &name, int latency_range_index) -{ - auto latency_json = json::array(); - - auto grouping = aggregated_data_.find(name); - - if (grouping != aggregated_data_.end()) - { - const auto &latency_samples = grouping->second.sample_latency_spans[latency_range_index]; - for (const auto &sample : latency_samples) - { - latency_json.push_back({ - {"spanid", std::string(reinterpret_cast(sample.GetSpanId().Id().data()))}, - {"parentid", - std::string(reinterpret_cast(sample.GetParentSpanId().Id().data()))}, - {"traceid", std::string(reinterpret_cast(sample.GetTraceId().Id().data()))}, - {"start", sample.GetStartTime().time_since_epoch().count()}, - {"duration", sample.GetDuration().count()}, - {"attributes", GetAttributesJSON(sample)}, - }); - } - } - return latency_json; -} - -json TracezHttpServer::GetAttributesJSON( - const opentelemetry::ext::zpages::ThreadsafeSpanData &sample) -{ - auto attributes_json = json::object(); - for (const auto &sample_attribute : sample.GetAttributes()) - { - auto &key = sample_attribute.first; - auto &val = sample_attribute.second; // OwnedAttributeValue - - /* Convert variant types to into their nonvariant form. This is done this way because - the frontend and JSON doesn't care about type, and variant's get function only allows - const integers or literals */ - - switch (val.index()) - { - case 0: - attributes_json[key] = nostd::get<0>(val); - break; - case 1: - attributes_json[key] = nostd::get<1>(val); - break; - case 2: - attributes_json[key] = nostd::get<2>(val); - break; - case 3: - attributes_json[key] = nostd::get<3>(val); - break; - case 4: - attributes_json[key] = nostd::get<4>(val); - break; - case 5: - attributes_json[key] = nostd::get<5>(val); - break; - case 6: - attributes_json[key] = nostd::get<6>(val); - break; - case 7: - attributes_json[key] = nostd::get<7>(val); - break; - case 8: - attributes_json[key] = nostd::get<8>(val); - break; - case 9: - attributes_json[key] = nostd::get<9>(val); - break; - } - } - return attributes_json; -} - -} // namespace zpages -} // namespace ext -OPENTELEMETRY_END_NAMESPACE diff --git a/ext/src/zpages/tracez_processor.cc b/ext/src/zpages/tracez_processor.cc deleted file mode 100644 index e7d68350d7..0000000000 --- a/ext/src/zpages/tracez_processor.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#include "opentelemetry/ext/zpages/tracez_processor.h" - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace ext -{ -namespace zpages -{ -namespace trace_sdk = opentelemetry::sdk::trace; - -void TracezSpanProcessor::OnStart(trace_sdk::Recordable &span, - const opentelemetry::trace::SpanContext & - /* parent_context */) noexcept -{ - shared_data_->OnStart(static_cast(&span)); -} - -void TracezSpanProcessor::OnEnd(std::unique_ptr &&span) noexcept -{ - shared_data_->OnEnd( - std::unique_ptr(static_cast(span.release()))); -} - -} // namespace zpages -} // namespace ext -OPENTELEMETRY_END_NAMESPACE diff --git a/ext/src/zpages/tracez_shared_data.cc b/ext/src/zpages/tracez_shared_data.cc deleted file mode 100644 index 4f4e4f5e91..0000000000 --- a/ext/src/zpages/tracez_shared_data.cc +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#include "opentelemetry/ext/zpages/tracez_shared_data.h" - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace ext -{ -namespace zpages -{ - -void TracezSharedData::OnStart(ThreadsafeSpanData *span) noexcept -{ - std::lock_guard lock(mtx_); - spans_.running.insert(span); -} - -void TracezSharedData::OnEnd(std::unique_ptr &&span) noexcept -{ - std::lock_guard lock(mtx_); - auto span_it = spans_.running.find(span.get()); - if (span_it != spans_.running.end()) - { - spans_.running.erase(span_it); - spans_.completed.push_back(std::unique_ptr(span.release())); - } -} - -TracezSharedData::CollectedSpans TracezSharedData::GetSpanSnapshot() noexcept -{ - CollectedSpans snapshot; - std::lock_guard lock(mtx_); - snapshot.running = spans_.running; - snapshot.completed = std::move(spans_.completed); - spans_.completed.clear(); - return snapshot; -} - -} // namespace zpages -} // namespace ext -OPENTELEMETRY_END_NAMESPACE diff --git a/ext/test/CMakeLists.txt b/ext/test/CMakeLists.txt index 2d2cc0d9fa..cc3d4cd1e1 100644 --- a/ext/test/CMakeLists.txt +++ b/ext/test/CMakeLists.txt @@ -1,9 +1,6 @@ # Copyright The OpenTelemetry Authors # SPDX-License-Identifier: Apache-2.0 -if(WITH_ZPAGES) - add_subdirectory(zpages) -endif() add_subdirectory(http) if(BUILD_W3CTRACECONTEXT_TEST) add_subdirectory(w3c_tracecontext_test) diff --git a/ext/test/zpages/BUILD b/ext/test/zpages/BUILD deleted file mode 100644 index e3371b8e93..0000000000 --- a/ext/test/zpages/BUILD +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright The OpenTelemetry Authors -# SPDX-License-Identifier: Apache-2.0 - -cc_test( - name = "threadsafe_span_data_tests", - srcs = [ - "threadsafe_span_data_test.cc", - ], - tags = ["test"], - deps = [ - "//ext/src/zpages", - "//sdk/src/trace", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "tracez_data_aggregator_tests", - srcs = [ - "tracez_data_aggregator_test.cc", - ], - tags = ["test"], - deps = [ - "//ext/src/zpages", - "//sdk/src/trace", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "tracez_processor_tests", - srcs = [ - "tracez_processor_test.cc", - ], - tags = ["test"], - deps = [ - "//ext/src/zpages", - "//sdk/src/trace", - "@com_google_googletest//:gtest_main", - ], -) diff --git a/ext/test/zpages/CMakeLists.txt b/ext/test/zpages/CMakeLists.txt deleted file mode 100644 index f3ecf5cdf2..0000000000 --- a/ext/test/zpages/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright The OpenTelemetry Authors -# SPDX-License-Identifier: Apache-2.0 - -foreach(testname tracez_processor_test tracez_data_aggregator_test - threadsafe_span_data_test) - add_executable(${testname} "${testname}.cc") - target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_zpages) - - gtest_add_tests( - TARGET ${testname} - TEST_PREFIX ext. - TEST_LIST ${testname}) -endforeach() diff --git a/ext/test/zpages/threadsafe_span_data_test.cc b/ext/test/zpages/threadsafe_span_data_test.cc deleted file mode 100644 index cee29672ed..0000000000 --- a/ext/test/zpages/threadsafe_span_data_test.cc +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#include "opentelemetry/ext/zpages/threadsafe_span_data.h" -#include "opentelemetry/nostd/variant.h" -#include "opentelemetry/trace/span_id.h" -#include "opentelemetry/trace/trace_id.h" - -#include -#include - -using opentelemetry::ext::zpages::ThreadsafeSpanData; -using opentelemetry::sdk::common::AttributeConverter; -using opentelemetry::sdk::common::OwnedAttributeValue; - -namespace trace_api = opentelemetry::trace; - -TEST(ThreadsafeSpanData, DefaultValues) -{ - trace_api::SpanContext empty_span_context{false, false}; - trace_api::SpanId zero_span_id; - ThreadsafeSpanData 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); -} - -TEST(ThreadsafeSpanData, 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}; - opentelemetry::common::SystemTimestamp now(std::chrono::system_clock::now()); - - ThreadsafeSpanData 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.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(opentelemetry::nostd::get(data.GetAttributes().at("attr1")), 314159); -} diff --git a/ext/test/zpages/tracez_data_aggregator_test.cc b/ext/test/zpages/tracez_data_aggregator_test.cc deleted file mode 100644 index 5139c9e914..0000000000 --- a/ext/test/zpages/tracez_data_aggregator_test.cc +++ /dev/null @@ -1,698 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#include "opentelemetry/ext/zpages/tracez_data_aggregator.h" - -#include - -#include "opentelemetry/ext/zpages/tracez_processor.h" -#include "opentelemetry/sdk/resource/resource.h" -#include "opentelemetry/sdk/trace/recordable.h" -#include "opentelemetry/sdk/trace/tracer.h" - -using namespace opentelemetry::sdk::trace; -using namespace opentelemetry::ext::zpages; -namespace nostd = opentelemetry::nostd; -namespace common = opentelemetry::common; -using opentelemetry::common::SteadyTimestamp; -using opentelemetry::trace::Span; - -const std::string span_name1 = "span 1"; -const std::string span_name2 = "span 2"; -const std::string span_name3 = "span 3"; - -/** - * TODO: Due to the absence of way to simulate the passing of time in the - * testing framework, synthetic delays had to be added in the tests to get the - * object in question to perform correctly. Later on if something like this is - * added the tests should be modified accordingly so that there is no external - * dependency. - * Additionally later on it would be better check for the span id(when set) - * rather than span name. - */ - -/** Test fixture for setting up the data aggregator and tracer for each test **/ -class TracezDataAggregatorTest : public ::testing::Test -{ -protected: - void SetUp() override - { - std::shared_ptr shared_data(new TracezSharedData()); - auto resource = opentelemetry::sdk::resource::Resource::Create({}); - std::unique_ptr processor(new TracezSpanProcessor(shared_data)); - std::vector> processors; - processors.push_back(std::move(processor)); - - auto context = std::make_shared(std::move(processors), resource); - tracer = std::shared_ptr(new Tracer(context)); - tracez_data_aggregator = std::unique_ptr( - new TracezDataAggregator(shared_data, milliseconds(10))); - } - - std::unique_ptr tracez_data_aggregator; - std::shared_ptr tracer; -}; - -/** - * Helper function to check if the counts of running, error and latency spans - * match what is expected - */ -void VerifySpanCountsInTracezData( - const std::string &span_name, - const TracezData &aggregated_data, - size_t running_span_count, - size_t error_span_count, - std::array completed_span_count_per_latency_bucket) -{ - // Asserts are needed to check the size of the container because they may need - // to be checked and if size checks fail it must be stopped - EXPECT_EQ(aggregated_data.running_span_count, running_span_count) - << " Count of running spans incorrect for " << span_name << "\n"; - - EXPECT_EQ(aggregated_data.sample_running_spans.size(), - std::min(running_span_count, kMaxNumberOfSampleSpans)) - << " Size of sample running spans incorrect for " << span_name << "\n"; - - EXPECT_EQ(aggregated_data.error_span_count, error_span_count) - << " Count of error spans incorrect for " << span_name << "\n"; - - EXPECT_EQ(aggregated_data.sample_error_spans.size(), - std::min(error_span_count, kMaxNumberOfSampleSpans)) - << " Count of running spans incorrect for " << span_name << "\n"; - - for (unsigned int boundary = 0; boundary < kLatencyBoundaries.size(); boundary++) - { - EXPECT_EQ(aggregated_data.completed_span_count_per_latency_bucket[boundary], - completed_span_count_per_latency_bucket[boundary]) - << " Count of completed spans in latency boundary " << boundary << " incorrect for " - << span_name << "\n"; - EXPECT_EQ(aggregated_data.sample_latency_spans[boundary].size(), - std::min(completed_span_count_per_latency_bucket[boundary], - kMaxNumberOfSampleSpans)) - << " Count of sample completed spans in latency boundary " << boundary << " incorrect for " - << span_name << "\n"; - } -} - -/**************************** No Span Test ************************************/ - -/** Test to check if data aggregator works as expected when there are no spans - * **/ -TEST_F(TracezDataAggregatorTest, NoSpans) -{ - auto data = tracez_data_aggregator->GetAggregatedTracezData(); - ASSERT_EQ(data.size(), 0); -} - -/*********************** Single span tests ************************************/ - -/** Test to check if data aggregator works as expected when there are - * is exactly a single running span **/ -TEST_F(TracezDataAggregatorTest, SingleRunningSpan) -{ - // Start the span get the data - auto span_first = tracer->StartSpan(span_name1); - std::this_thread::sleep_for(milliseconds(500)); - auto data = tracez_data_aggregator->GetAggregatedTracezData(); - - // Check to see if span name exists - ASSERT_EQ(data.size(), 1); - ASSERT_TRUE(data.find(span_name1) != data.end()); - auto &aggregated_data = data.at(span_name1); - - // Verify span counts then content of spans - VerifySpanCountsInTracezData(span_name1, aggregated_data, 1, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0}); - - ASSERT_EQ(aggregated_data.sample_running_spans.size(), 1); - ASSERT_EQ(aggregated_data.sample_running_spans.front().GetName().data(), span_name1); - span_first->End(); -} - -/** Test to check if data aggregator works as expected when there is exactly one - * completed span **/ -TEST_F(TracezDataAggregatorTest, SingleCompletedSpan) -{ - // Start and end the span at a specified times - opentelemetry::trace::StartSpanOptions start; - start.start_steady_time = SteadyTimestamp(nanoseconds(10)); - opentelemetry::trace::EndSpanOptions end; - end.end_steady_time = SteadyTimestamp(nanoseconds(40)); - tracer->StartSpan(span_name1, start)->End(end); - - // Get the data and make sure span name exists in the data - std::this_thread::sleep_for(milliseconds(500)); - auto data = tracez_data_aggregator->GetAggregatedTracezData(); - ASSERT_EQ(data.size(), 1); - ASSERT_TRUE(data.find(span_name1) != data.end()); - - auto &aggregated_data = data.at(span_name1); - // Make sure counts of spans are in order - VerifySpanCountsInTracezData(span_name1, aggregated_data, 0, 0, {1, 0, 0, 0, 0, 0, 0, 0, 0}); - - // Check if the span is correctly updated in the first boundary - ASSERT_EQ(aggregated_data.sample_latency_spans[LatencyBoundary::k0MicroTo10Micro].size(), 1); - ASSERT_EQ(aggregated_data.sample_latency_spans[LatencyBoundary::k0MicroTo10Micro] - .front() - .GetDuration() - .count(), - 30); -} - -/** Test to check if data aggregator works as expected when there is exactly - * one error span **/ -TEST_F(TracezDataAggregatorTest, SingleErrorSpan) -{ - // Start and end a single error span - auto span = tracer->StartSpan(span_name1); - span->SetStatus(opentelemetry::trace::StatusCode::kError, "span cancelled"); - span->End(); - std::this_thread::sleep_for(milliseconds(500)); - auto data = tracez_data_aggregator->GetAggregatedTracezData(); - - // Check to see if span name can be found in aggregation - ASSERT_EQ(data.size(), 1); - ASSERT_TRUE(data.find(span_name1) != data.end()); - - auto &aggregated_data = data.at(span_name1); - // Make sure counts of spans are in order - VerifySpanCountsInTracezData(span_name1, aggregated_data, 0, 1, {0, 0, 0, 0, 0, 0, 0, 0, 0}); - - // Check the value of the error span introduced - ASSERT_EQ(aggregated_data.sample_error_spans.size(), 1); - ASSERT_EQ(aggregated_data.sample_error_spans.front().GetName().data(), span_name1); -} - -/************************* Multiple span tests ********************************/ - -/** Test to check if multiple running spans behaves as expected**/ -TEST_F(TracezDataAggregatorTest, MultipleRunningSpans) -{ - // A container that maps a span name to the number of spans to start with that - // span name - std::unordered_map running_span_name_to_count({ - {span_name1, 1}, - {span_name2, 2}, - {span_name3, 3}, - }); - - // Start and store spans based on the above map - std::vector> running_span_container; - for (auto span_name : running_span_name_to_count) - { - for (int count = 0; count < span_name.second; count++) - running_span_container.push_back(tracer->StartSpan(span_name.first)); - } - - // give time for aggregation and then get data - std::this_thread::sleep_for(milliseconds(500)); - auto data = tracez_data_aggregator->GetAggregatedTracezData(); - ASSERT_EQ(data.size(), running_span_name_to_count.size()); - - // Check to see if the running span counts were updated correctly - for (auto &span_name : running_span_name_to_count) - { - ASSERT_TRUE(data.find(span_name.first) != data.end()); - - // Make sure counts of spans are in order - VerifySpanCountsInTracezData(span_name.first, data.at(span_name.first), span_name.second, 0, - {0, 0, 0, 0, 0, 0, 0, 0, 0}); - - ASSERT_EQ(data.at(span_name.first).sample_running_spans.size(), span_name.second); - for (auto &span_sample : data.at(span_name.first).sample_running_spans) - { - ASSERT_EQ(span_sample.GetName().data(), span_name.first); - } - } - - for (auto i = running_span_container.begin(); i != running_span_container.end(); i++) - (*i)->End(); -} - -/** Test to check if multiple completed spans updates the aggregated data - * correctly **/ -TEST_F(TracezDataAggregatorTest, MultipleCompletedSpan) -{ - // Start spans with span name and the corresponding durations in one of the 9 - // latency buckets - const std::unordered_map>> - span_name_to_duration( - {{span_name1, {{nanoseconds(10), nanoseconds(4600)}, {}, {}, {}, {}, {}, {}, {}, {}}}, - {span_name2, - {{}, - {nanoseconds(38888), nanoseconds(98768)}, - {nanoseconds(983251)}, - {}, - {}, - {}, - {}, - {}, - {}}}, - {span_name3, - {{}, - {}, - {}, - {nanoseconds(1234567), nanoseconds(1234567)}, - {}, - {}, - {}, - {}, - {nanoseconds(9999999999999)}}}}); - opentelemetry::trace::StartSpanOptions start; - opentelemetry::trace::EndSpanOptions end; - for (auto &span : span_name_to_duration) - { - for (auto &buckets : span.second) - { - for (auto &duration : buckets) - { - long long int end_time = duration.count() + 1; - start.start_steady_time = SteadyTimestamp(nanoseconds(1)); - end.end_steady_time = SteadyTimestamp(nanoseconds(end_time)); - tracer->StartSpan(span.first, start)->End(end); - } - } - } - - // Give time for aggregation and get data - std::this_thread::sleep_for(milliseconds(500)); - auto data = tracez_data_aggregator->GetAggregatedTracezData(); - - ASSERT_EQ(data.size(), span_name_to_duration.size()); - - for (auto &span : span_name_to_duration) - { - ASSERT_TRUE(data.find(span.first) != data.end()); - auto &aggregated_data = data.at(span.first); - - // Make sure counts of spans are in order - VerifySpanCountsInTracezData( - span.first, aggregated_data, 0, 0, - {(unsigned int)span.second[0].size(), (unsigned int)span.second[1].size(), - (unsigned int)span.second[2].size(), (unsigned int)span.second[3].size(), - (unsigned int)span.second[4].size(), (unsigned int)span.second[5].size(), - (unsigned int)span.second[6].size(), (unsigned int)span.second[7].size(), - (unsigned int)span.second[8].size()}); - - for (unsigned int boundary = 0; boundary < kLatencyBoundaries.size(); boundary++) - { - ASSERT_EQ(aggregated_data.sample_latency_spans[boundary].size(), - span.second[boundary].size()); - auto latency_sample = aggregated_data.sample_latency_spans[boundary].begin(); - for (unsigned int idx = 0; idx < span.second[boundary].size(); idx++) - { - ASSERT_EQ(span.second[boundary][idx].count(), latency_sample->GetDuration().count()); - latency_sample = std::next(latency_sample); - } - } - } -} - -/** - * This test checks to see if the aggregated data is updated correctly - * when there are multiple error spans. - * It checks both the count of error spans and the error samples - */ -TEST_F(TracezDataAggregatorTest, MultipleErrorSpans) -{ - // Container to store the span names --> error messges for the span name - std::unordered_map> span_name_to_error( - {{span_name1, {"span 1 error"}}, - {span_name2, {"span 2 error 1", "span 2 error 2"}}, - {span_name3, - {"span 3 error 1", "span 3 error 2", "span 3 error 3", "span 3 error 4", - "span 3 error 5"}}}); - - // Start spans with the error messages based on the map - for (auto &span_error : span_name_to_error) - { - for (auto error_desc : span_error.second) - { - auto span = tracer->StartSpan(span_error.first); - span->SetStatus(opentelemetry::trace::StatusCode::kError, error_desc); - span->End(); - } - } - - // Give some time and then get data - std::this_thread::sleep_for(milliseconds(500)); - auto data = tracez_data_aggregator->GetAggregatedTracezData(); - ASSERT_EQ(data.size(), span_name_to_error.size()); - - // Check if error spans were updated correctly for the different span names - for (auto &span_error : span_name_to_error) - { - // First try to find the span name in aggregation, then check the count of - // the error spans and then check values - ASSERT_TRUE(data.find(span_error.first) != data.end()); - - auto &aggregated_data = data.at(span_error.first); - - // Make sure counts of spans are in order - VerifySpanCountsInTracezData(span_error.first, aggregated_data, 0, span_error.second.size(), - {0, 0, 0, 0, 0, 0, 0, 0, 0}); - ASSERT_EQ(aggregated_data.error_span_count, span_error.second.size()); - - auto error_sample = aggregated_data.sample_error_spans.begin(); - for (unsigned int idx = 0; idx < span_error.second.size(); idx++) - { - ASSERT_EQ(span_error.second[idx], error_sample->GetDescription()); - error_sample = std::next(error_sample); - } - } -} - -/************************ Sample spans tests **********************************/ - -/** - * This test checks to see that the maximum number of running samples(5) for a - * bucket is not exceeded. If there are more spans than this for a single bucket - * it removes the earliest span that was received - */ -TEST_F(TracezDataAggregatorTest, RunningSampleSpansOverCapacity) -{ - int running_span_count = 6; - // Start and store spans based on the above map - std::vector> running_span_container; - for (int count = 0; count < running_span_count; count++) - running_span_container.push_back(tracer->StartSpan(span_name1)); - - std::this_thread::sleep_for(milliseconds(500)); - // Fetch data and check if span name is spresent - auto data = tracez_data_aggregator->GetAggregatedTracezData(); - ASSERT_EQ(data.size(), 1); - ASSERT_TRUE(data.find(span_name1) != data.end()); - - // Check if error spans are updated according to spans started - auto &aggregated_data = data.at(span_name1); - VerifySpanCountsInTracezData(span_name1, aggregated_data, 6, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0}); - - ASSERT_EQ(aggregated_data.sample_running_spans.size(), kMaxNumberOfSampleSpans); - - for (auto i = running_span_container.begin(); i != running_span_container.end(); i++) - { - (*i)->End(); - } -} - -/** - * This test checks to see that the maximum number of error samples(5) for a - * bucket is not exceeded. If there are more spans than this for a single bucket - * it removes the earliest span that was received - */ -TEST_F(TracezDataAggregatorTest, ErrorSampleSpansOverCapacity) -{ - // Create error spans with the descriptions in the vector - std::vector span_error_descriptions = {"error span 1", "error span 2", - "error span 3", "error span 4", - "error span 5", "error span 6"}; - for (auto span_error_description : span_error_descriptions) - { - auto span = tracer->StartSpan(span_name1); - span->SetStatus(opentelemetry::trace::StatusCode::kError, span_error_description); - span->End(); - } - - std::this_thread::sleep_for(milliseconds(500)); - - // Fetch data and check if span name is spresent - auto data = tracez_data_aggregator->GetAggregatedTracezData(); - ASSERT_EQ(data.size(), 1); - ASSERT_TRUE(data.find(span_name1) != data.end()); - - std::this_thread::sleep_for(milliseconds(500)); - - // Check if error spans are updated according to spans started - auto &aggregated_data = data.at(span_name1); - VerifySpanCountsInTracezData(span_name1, aggregated_data, 0, span_error_descriptions.size(), - {0, 0, 0, 0, 0, 0, 0, 0, 0}); - - // Check if the latest 5 error spans exist out of the total 6 that were - // introduced - auto error_sample = aggregated_data.sample_error_spans.begin(); - for (unsigned int idx = 1; idx < span_error_descriptions.size(); idx++) - { - ASSERT_EQ(error_sample->GetDescription(), span_error_descriptions[idx]); - error_sample = std::next(error_sample); - } -} - -/** - * This test checks to see that the maximum number of latency samples(5) for a - * bucket is not exceeded. If there are more spans than this for a single bucket - * it removes the earliest span that was received - */ -TEST_F(TracezDataAggregatorTest, CompletedSampleSpansOverCapacity) -{ - opentelemetry::trace::StartSpanOptions start; - opentelemetry::trace::EndSpanOptions end; - - // Start and end 6 spans with the same name that fall into the first latency - // bucket - std::vector> timestamps = { - make_pair(nanoseconds(10), nanoseconds(100)), - make_pair(nanoseconds(1), nanoseconds(10000)), - make_pair(nanoseconds(1000), nanoseconds(3000)), - make_pair(nanoseconds(12), nanoseconds(12)), - make_pair(nanoseconds(10), nanoseconds(5000)), - make_pair(nanoseconds(10), nanoseconds(60))}; - for (auto timestamp : timestamps) - { - start.start_steady_time = SteadyTimestamp(timestamp.first); - end.end_steady_time = SteadyTimestamp(timestamp.second); - tracer->StartSpan(span_name1, start)->End(end); - } - - // Give some time and get data - std::this_thread::sleep_for(milliseconds(500)); - auto data = tracez_data_aggregator->GetAggregatedTracezData(); - - ASSERT_EQ(data.size(), 1); - ASSERT_TRUE(data.find(span_name1) != data.end()); - - std::this_thread::sleep_for(milliseconds(500)); - auto &aggregated_data = data.at(span_name1); - VerifySpanCountsInTracezData(span_name1, aggregated_data, 0, 0, - {(unsigned int)timestamps.size(), 0, 0, 0, 0, 0, 0, 0, 0}); - - // Check the count of completed spans in the buckets and the samples stored - auto latency_sample = - aggregated_data.sample_latency_spans[LatencyBoundary::k0MicroTo10Micro].begin(); - - // idx starts from 1 and not 0 because there are 6 completed spans in the same - // bucket the and the first one is removed - for (unsigned int idx = 1; idx < timestamps.size(); idx++) - { - ASSERT_EQ(latency_sample->GetDuration().count(), - timestamps[idx].second.count() - timestamps[idx].first.count()); - latency_sample = std::next(latency_sample); - } -} - -/************************* Miscellaneous tests ********************************/ - -/** Test to see if the span names are in alphabetical order **/ -TEST_F(TracezDataAggregatorTest, SpanNameInAlphabeticalOrder) -{ - std::vector span_names = {span_name1, span_name2, span_name3}; - - auto span_first = tracer->StartSpan(span_name2); - tracer->StartSpan(span_name1)->End(); - auto span_third = tracer->StartSpan(span_name3); - span_third->SetStatus(opentelemetry::trace::StatusCode::kError, "span cancelled"); - span_third->End(); - std::this_thread::sleep_for(milliseconds(500)); - // Get data and check if span name exists in aggregation - auto data = tracez_data_aggregator->GetAggregatedTracezData(); - ASSERT_EQ(data.size(), span_names.size()); - - int span_names_idx = 0; - for (auto &spans : data) - { - ASSERT_EQ(spans.first, span_names[span_names_idx]); - span_names_idx++; - } - span_first->End(); -} - -/** This test checks to see that there is no double counting of running spans - * when get aggregated data is called twice**/ -TEST_F(TracezDataAggregatorTest, AdditionToRunningSpans) -{ - // Start a span and check the data - auto span_first = tracer->StartSpan(span_name1); - std::this_thread::sleep_for(milliseconds(500)); - auto data = tracez_data_aggregator->GetAggregatedTracezData(); - ASSERT_EQ(data.size(), 1); - ASSERT_TRUE(data.find(span_name1) != data.end()); - VerifySpanCountsInTracezData(span_name1, data.at(span_name1), 1, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0}); - - // Start another span and check to see if there is no double counting of spans - auto span_second = tracer->StartSpan(span_name1); - - // Give some time and get updated data - std::this_thread::sleep_for(milliseconds(500)); - data = tracez_data_aggregator->GetAggregatedTracezData(); - ASSERT_EQ(data.size(), 1); - ASSERT_TRUE(data.find(span_name1) != data.end()); - auto &aggregated_data = data.at(span_name1); - VerifySpanCountsInTracezData(span_name1, aggregated_data, 2, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0}); - - ASSERT_EQ(aggregated_data.sample_running_spans.size(), 2); - for (auto &sample_span : aggregated_data.sample_running_spans) - { - ASSERT_EQ(sample_span.GetName().data(), span_name1); - } - span_first->End(); - span_second->End(); -} - -/** This test checks to see that once a running span is completed it the - * aggregated data is updated correctly **/ -TEST_F(TracezDataAggregatorTest, RemovalOfRunningSpanWhenCompleted) -{ - opentelemetry::trace::StartSpanOptions start; - start.start_steady_time = SteadyTimestamp(nanoseconds(10)); - opentelemetry::trace::EndSpanOptions end; - end.end_steady_time = SteadyTimestamp(nanoseconds(40)); - - // Start a span and make sure data is updated - auto span_first = tracer->StartSpan(span_name1, start); - std::this_thread::sleep_for(milliseconds(500)); - auto data = tracez_data_aggregator->GetAggregatedTracezData(); - ASSERT_EQ(data.size(), 1); - ASSERT_TRUE(data.find(span_name1) != data.end()); - VerifySpanCountsInTracezData(span_name1, data.at(span_name1), 1, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0}); - ASSERT_EQ(data.at(span_name1).sample_running_spans.front().GetName().data(), span_name1); - // End the span and make sure running span is removed and completed span is - // updated, there should be only one completed span - span_first->End(end); - std::this_thread::sleep_for(milliseconds(500)); - - // Make sure sample span still exists before next aggregation - ASSERT_TRUE(data.find(span_name1) != data.end()); - ASSERT_EQ(data.at(span_name1).sample_running_spans.front().GetName().data(), span_name1); - - data = tracez_data_aggregator->GetAggregatedTracezData(); - - ASSERT_EQ(data.size(), 1); - ASSERT_TRUE(data.find(span_name1) != data.end()); - - // Check if completed span fields are correctly updated - auto &aggregated_data = data.at(span_name1); - VerifySpanCountsInTracezData(span_name1, aggregated_data, 0, 0, {1, 0, 0, 0, 0, 0, 0, 0, 0}); - ASSERT_EQ(aggregated_data.sample_latency_spans[LatencyBoundary::k0MicroTo10Micro] - .front() - .GetDuration() - .count(), - 30); -} - -TEST_F(TracezDataAggregatorTest, RunningSpanChangesNameBeforeCompletion) -{ - opentelemetry::trace::StartSpanOptions start; - start.start_steady_time = SteadyTimestamp(nanoseconds(10)); - opentelemetry::trace::EndSpanOptions end; - end.end_steady_time = SteadyTimestamp(nanoseconds(40)); - - // Start a span and make sure data is updated - auto span_first = tracer->StartSpan(span_name1, start); - std::this_thread::sleep_for(milliseconds(500)); - auto data = tracez_data_aggregator->GetAggregatedTracezData(); - ASSERT_EQ(data.size(), 1); - ASSERT_TRUE(data.find(span_name1) != data.end()); - VerifySpanCountsInTracezData(span_name1, data.at(span_name1), 1, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0}); - ASSERT_EQ(data.at(span_name1).sample_running_spans.front().GetName().data(), span_name1); - - // End the span and make sure running span is removed and completed span is - // updated, there should be only one completed span - span_first->UpdateName(span_name2); - span_first->End(end); - - // Check if sample span is present before fetching updated data - std::this_thread::sleep_for(milliseconds(500)); - ASSERT_TRUE(data.find(span_name1) != data.end()); - ASSERT_EQ(data.at(span_name1).sample_running_spans.front().GetName(), span_name1); - - data = tracez_data_aggregator->GetAggregatedTracezData(); - - ASSERT_EQ(data.size(), 1); - ASSERT_TRUE(data.find(span_name2) != data.end()); - - // Check if completed span fields are correctly updated - auto &aggregated_data = data.at(span_name2); - VerifySpanCountsInTracezData(span_name2, aggregated_data, 0, 0, {1, 0, 0, 0, 0, 0, 0, 0, 0}); - ASSERT_EQ(aggregated_data.sample_latency_spans[LatencyBoundary::k0MicroTo10Micro] - .front() - .GetDuration() - .count(), - 30); -} - -/** Test to check if the span latencies with duration at the edge of boundaries - * fall in the correct bucket **/ -TEST_F(TracezDataAggregatorTest, EdgeSpanLatenciesFallInCorrectBoundaries) -{ - opentelemetry::trace::StartSpanOptions start; - opentelemetry::trace::EndSpanOptions end; - - // Start and end 6 spans with the same name that fall into the first latency - // bucket - std::vector durations = { - nanoseconds(0), nanoseconds(10000), nanoseconds(100000), - nanoseconds(1000000), nanoseconds(10000000), nanoseconds(100000000), - nanoseconds(1000000000), nanoseconds(10000000000), nanoseconds(100000000000)}; - for (auto duration : durations) - { - start.start_steady_time = SteadyTimestamp(nanoseconds(1)); - end.end_steady_time = SteadyTimestamp(nanoseconds(duration.count() + 1)); - tracer->StartSpan(span_name1, start)->End(end); - } - - std::this_thread::sleep_for(milliseconds(500)); - auto data = tracez_data_aggregator->GetAggregatedTracezData(); - ASSERT_EQ(data.size(), 1); - ASSERT_TRUE(data.find(span_name1) != data.end()); - - std::this_thread::sleep_for(milliseconds(500)); - auto &aggregated_data = data.at(span_name1); - VerifySpanCountsInTracezData(span_name1, aggregated_data, 0, 0, {1, 1, 1, 1, 1, 1, 1, 1, 1}); - - // Check if the latency boundary is updated correctly - for (unsigned int boundary = 0; boundary < kLatencyBoundaries.size(); boundary++) - { - ASSERT_EQ(aggregated_data.sample_latency_spans[boundary].front().GetDuration().count(), - durations[boundary].count()); - } -} - -/** This test makes sure that the data is consistent when there are multiple - * calls to the data aggegator with no change in data **/ -TEST_F(TracezDataAggregatorTest, NoChangeInBetweenCallsToAggregator) -{ - opentelemetry::trace::StartSpanOptions start; - start.start_steady_time = SteadyTimestamp(nanoseconds(1)); - - opentelemetry::trace::EndSpanOptions end; - end.end_steady_time = SteadyTimestamp(nanoseconds(1)); - - tracer->StartSpan(span_name1, start)->End(end); - auto running_span = tracer->StartSpan(span_name2); - auto span = tracer->StartSpan(span_name3); - span->SetStatus(opentelemetry::trace::StatusCode::kError, "span cancelled"); - span->End(); - std::this_thread::sleep_for(milliseconds(500)); - auto data = tracez_data_aggregator->GetAggregatedTracezData(); - std::this_thread::sleep_for(milliseconds(500)); - // Get data and check if span name exists in aggregation - data = tracez_data_aggregator->GetAggregatedTracezData(); - ASSERT_TRUE(data.find(span_name1) != data.end()); - VerifySpanCountsInTracezData(span_name1, data.at(span_name1), 0, 0, {1, 0, 0, 0, 0, 0, 0, 0, 0}); - - ASSERT_TRUE(data.find(span_name2) != data.end()); - VerifySpanCountsInTracezData(span_name2, data.at(span_name2), 1, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0}); - - ASSERT_TRUE(data.find(span_name3) != data.end()); - VerifySpanCountsInTracezData(span_name3, data.at(span_name3), 0, 1, {0, 0, 0, 0, 0, 0, 0, 0, 0}); - - running_span->End(); -} diff --git a/ext/test/zpages/tracez_processor_test.cc b/ext/test/zpages/tracez_processor_test.cc deleted file mode 100644 index 6cc3640651..0000000000 --- a/ext/test/zpages/tracez_processor_test.cc +++ /dev/null @@ -1,647 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#include "opentelemetry/ext/zpages/tracez_processor.h" - -#include - -#include - -#include "opentelemetry/ext/zpages/threadsafe_span_data.h" -#include "opentelemetry/nostd/span.h" -#include "opentelemetry/sdk/resource/resource.h" -#include "opentelemetry/sdk/trace/tracer.h" - -using namespace opentelemetry::sdk::trace; -using namespace opentelemetry::ext::zpages; -using namespace testing; - -//////////////////////////////////// TEST HELPER FUNCTIONS ////////////////////////////// - -/* - * Helper function uses the current processor to update spans contained in completed_spans - * and running_spans. completed_spans contains all spans (cumulative), unless marked otherwise - */ -void UpdateSpans(std::shared_ptr &data, - std::vector> &completed, - std::unordered_set &running, - bool store_only_new_completed = false) -{ - auto spans = data->GetSpanSnapshot(); - running = spans.running; - if (store_only_new_completed) - { - completed.clear(); - completed = std::move(spans.completed); - } - else - { - std::move(spans.completed.begin(), spans.completed.end(), - std::inserter(completed, completed.end())); - } - spans.completed.clear(); -} - -/* - * Returns true if all the span names in the name vector within the given range appears in - * at least the same frequency as they do in running_spans. - * - * If no start value is given, start at index 0 - * If no end value is given, end at name vector end - * If 1-1 correspondance marked, return true if completed has all names in same frequency, - * no more or less - */ -bool ContainsNames(const std::vector &names, - std::unordered_set &running, - size_t name_start = 0, - size_t name_end = 0, - bool one_to_one_correspondence = false) -{ - if (name_end == 0) - name_end = names.size(); - - size_t num_names = name_end - name_start; - - if (num_names > running.size() || // More names than spans, can't have all names - (one_to_one_correspondence && num_names != running.size())) - { - return false; - } - std::vector is_contained(num_names, false); - - // Mark all names that are contained only once - // in the order they appear - for (auto &span : running) - { - for (unsigned int i = 0; i < num_names; i++) - { - if (span->GetName() == names[name_start + i] && !is_contained[i]) - { - is_contained[i] = true; - break; - } - } - } - - for (auto &&b : is_contained) - if (!b) - return false; - - return true; -} - -/* - * Returns true if all the span names in the nam vector within the given range appears in - * at least the same frequency as they do in completed_spans - * - * If no start value is given, start at index 0 - * If no end value is given, end at name vector end - * If 1-1 correspondance marked, return true if completed has all names in same frequency, - * no more or less - */ -bool ContainsNames(const std::vector &names, - std::vector> &completed, - size_t name_start = 0, - size_t name_end = 0, - bool one_to_one_correspondence = false) -{ - - if (name_end == 0) - name_end = names.size(); - - size_t num_names = name_end - name_start; - - if (num_names > completed.size() || (one_to_one_correspondence && num_names != completed.size())) - { - return false; - } - std::vector is_contained(num_names, false); - - for (auto &span : completed) - { - for (unsigned int i = 0; i < num_names; i++) - { - if (span->GetName() == names[name_start + i] && !is_contained[i]) - { - is_contained[i] = true; - break; - } - } - } - - for (auto &&b : is_contained) - if (!b) - return false; - - return true; -} - -/* - * Helper function calls GetSpanSnapshot() i times and does nothing with it - * otherwise. Used for testing thread safety - */ -void GetManySnapshots(std::shared_ptr &data, int i) -{ - for (; i > 0; i--) - data->GetSpanSnapshot(); -} - -/* - * Helper function that creates i spans, which are added into the passed - * in vector. Used for testing thread safety - */ -void StartManySpans( - std::vector> &spans, - std::shared_ptr tracer, - int i) -{ - for (; i > 0; i--) - spans.push_back(tracer->StartSpan("span")); -} - -/* - * Helper function that ends all spans in the passed in span vector. Used - * for testing thread safety - */ -void EndAllSpans(std::vector> &spans) -{ - for (auto &span : spans) - span->End(); -} - -//////////////////////////////// TEST FIXTURE ////////////////////////////////////// - -/* - * Reduce code duplication by having single area with shared setup code - */ -class TracezProcessor : public ::testing::Test -{ -protected: - void SetUp() override - { - shared_data = std::shared_ptr(new TracezSharedData()); - processor = std::shared_ptr(new TracezSpanProcessor(shared_data)); - std::unique_ptr processor2(new TracezSpanProcessor(shared_data)); - std::vector> processors; - processors.push_back(std::move(processor2)); - auto resource = opentelemetry::sdk::resource::Resource::Create({}); - - // Note: we make a *different* processor for the tracercontext. THis is because - // all the tests use shared data, and we want to make sure this works correctly. - auto context = std::make_shared(std::move(processors), resource); - - tracer = std::shared_ptr(new Tracer(context)); - auto spans = shared_data->GetSpanSnapshot(); - running = spans.running; - completed = std::move(spans.completed); - - span_names = {"s0", "s2", "s1", "s1", "s"}; - } - - std::shared_ptr shared_data; - std::shared_ptr processor; - std::shared_ptr tracer; - - std::vector span_names; - std::vector> span_vars; - - std::unordered_set running; - std::vector> completed; -}; - -///////////////////////////////////////// TESTS /////////////////////////////////// - -/* - * Test if both span containers are empty when no spans exist or are added. - * Ensures no rogue spans appear in the containers somehow. - */ -TEST_F(TracezProcessor, NoSpans) -{ - auto recordable = processor->MakeRecordable(); - - EXPECT_EQ(running.size(), 0); - EXPECT_EQ(completed.size(), 0); -} - -/* - * Test if a single span moves from running to completed at expected times. - * All completed spans are stored. Ensures basic functionality and that accumulation - * can happen - */ -TEST_F(TracezProcessor, OneSpanCumulative) -{ - auto span = tracer->StartSpan(span_names[0]); - UpdateSpans(shared_data, completed, running); - - EXPECT_TRUE(ContainsNames(span_names, running, 0, 1, true)); - EXPECT_EQ(running.size(), 1); - EXPECT_EQ(completed.size(), 0); - - span->End(); - UpdateSpans(shared_data, completed, running); - - EXPECT_TRUE(ContainsNames(span_names, completed, 0, 1, true)); - EXPECT_EQ(running.size(), 0); - EXPECT_EQ(completed.size(), 1); -} - -/* - * Test if multiple spans move from running to completed at expected times. Check if - * all are in a container, either running/completed during checks. Ensures basic functionality - * and that accumulation can happen for many spans - * All completed spans are stored. - */ -TEST_F(TracezProcessor, MultipleSpansCumulative) -{ - EXPECT_EQ(running.size(), 0); - EXPECT_EQ(completed.size(), 0); - - // Start and store spans using span_names - for (const auto &name : span_names) - span_vars.push_back(tracer->StartSpan(name)); - UpdateSpans(shared_data, completed, running); - - EXPECT_TRUE(ContainsNames(span_names, running)); // s0 s2 s1 s1 s - EXPECT_EQ(running.size(), span_names.size()); - EXPECT_EQ(completed.size(), 0); - - // End all spans - for (auto &span : span_vars) - span->End(); - UpdateSpans(shared_data, completed, running); - - EXPECT_TRUE(ContainsNames(span_names, completed)); // s0 s2 s1 s1 s - EXPECT_EQ(running.size(), 0); - EXPECT_EQ(completed.size(), span_names.size()); -} - -/* - * Test if multiple spans move from running to completed at expected times, - * running/completed spans are split. Middle spans end first. Ensures basic functionality - * and that accumulation can happen for many spans even spans that start and end non- - * sequentially. All completed spans are stored. - */ -TEST_F(TracezProcessor, MultipleSpansMiddleSplitCumulative) -{ - for (const auto &name : span_names) - span_vars.push_back(tracer->StartSpan(name)); - UpdateSpans(shared_data, completed, running); - - EXPECT_TRUE(ContainsNames(span_names, running)); // s0 s2 s1 s1 s - EXPECT_EQ(running.size(), span_names.size()); - EXPECT_EQ(completed.size(), 0); - - // End 4th span - span_vars[3]->End(); - UpdateSpans(shared_data, completed, running); - - EXPECT_TRUE(ContainsNames(span_names, running, 0, 3)); // s0 s2 s1 - EXPECT_TRUE(ContainsNames(span_names, running, 4)); // + s - EXPECT_TRUE(ContainsNames(span_names, completed, 3, 4)); // s1 - EXPECT_EQ(running.size(), 4); - EXPECT_EQ(completed.size(), 1); - - // End 2nd span - span_vars[1]->End(); - UpdateSpans(shared_data, completed, running); - - EXPECT_TRUE(ContainsNames(span_names, running, 0, 1)); // s0 - EXPECT_TRUE(ContainsNames(span_names, running, 2, 3)); // + s1 - EXPECT_TRUE(ContainsNames(span_names, running, 4)); // + s - EXPECT_TRUE(ContainsNames(span_names, completed, 1, 2)); // s2 - EXPECT_TRUE(ContainsNames(span_names, completed, 3, 4)); // s1 - EXPECT_EQ(running.size(), 3); - EXPECT_EQ(completed.size(), 2); - - // End 3rd span (last middle span) - span_vars[2]->End(); - UpdateSpans(shared_data, completed, running); - - EXPECT_TRUE(ContainsNames(span_names, running, 0, 1)); // s0 - EXPECT_TRUE(ContainsNames(span_names, running, 4)); // + s - EXPECT_TRUE(ContainsNames(span_names, completed, 1, 4)); // s2 s1 s1 - EXPECT_EQ(running.size(), 2); - EXPECT_EQ(completed.size(), 3); - - // End remaining Spans - span_vars[0]->End(); - span_vars[4]->End(); - UpdateSpans(shared_data, completed, running); - - EXPECT_TRUE(ContainsNames(span_names, completed)); // s0 s2 s1 s1 s - EXPECT_EQ(running.size(), 0); - EXPECT_EQ(completed.size(), 5); -} - -/* - * Test if multiple spans move from running to completed at expected times, - * running/completed spans are split. Ensures basic functionality and that - * accumulation can happen for many spans even spans that start and end non- - * sequentially. All completed spans are stored. - */ -TEST_F(TracezProcessor, MultipleSpansOuterSplitCumulative) -{ - for (const auto &name : span_names) - span_vars.push_back(tracer->StartSpan(name)); - - // End last span - span_vars[4]->End(); - UpdateSpans(shared_data, completed, running); - - EXPECT_TRUE(ContainsNames(span_names, running, 0, 4)); // s0 s2 s1 s1 - EXPECT_TRUE(ContainsNames(span_names, completed, 4)); // s - EXPECT_EQ(running.size(), 4); - EXPECT_EQ(completed.size(), 1); - - // End first span - span_vars[0]->End(); - UpdateSpans(shared_data, completed, running); - - EXPECT_TRUE(ContainsNames(span_names, running, 1, 4)); // s2 s1 s1 - EXPECT_TRUE(ContainsNames(span_names, completed, 0, 1)); // s0 - EXPECT_TRUE(ContainsNames(span_names, completed, 4)); // s - EXPECT_EQ(running.size(), 3); - EXPECT_EQ(completed.size(), 2); - - // End remaining Spans - for (int i = 1; i < 4; i++) - span_vars[i]->End(); - UpdateSpans(shared_data, completed, running); - - EXPECT_TRUE(ContainsNames(span_names, completed)); // s0 s2 s1 s1 s - EXPECT_EQ(running.size(), 0); - EXPECT_EQ(completed.size(), 5); -} - -/* - * Test if a single span moves from running to completed at expected times. - * Ensure correct behavior even when spans are discarded. Only new completed - * spans are stored. - */ -TEST_F(TracezProcessor, OneSpanNewOnly) -{ - auto span = tracer->StartSpan(span_names[0]); - UpdateSpans(shared_data, completed, running, true); - - EXPECT_TRUE(ContainsNames(span_names, running, 0, 1, true)); - EXPECT_EQ(running.size(), 1); - EXPECT_EQ(completed.size(), 0); - - span->End(); - UpdateSpans(shared_data, completed, running, true); - - EXPECT_TRUE(ContainsNames(span_names, completed, 0, 1, true)); - EXPECT_EQ(running.size(), 0); - EXPECT_EQ(completed.size(), 1); - - UpdateSpans(shared_data, completed, running, true); - - EXPECT_EQ(running.size(), 0); - EXPECT_EQ(completed.size(), 0); -} - -/* - * Test if multiple spans move from running to completed at expected times, - * running/completed spans are split. Middle spans end first. Ensure correct - * behavior even when multiple spans are discarded, even when span starting and - * ending is non-sequential. Only new completed spans are stored. - */ -TEST_F(TracezProcessor, MultipleSpansMiddleSplitNewOnly) -{ - for (const auto &name : span_names) - span_vars.push_back(tracer->StartSpan(name)); - UpdateSpans(shared_data, completed, running); - - EXPECT_TRUE(ContainsNames(span_names, running, 0, 5, true)); // s0 s2 s1 s1 s - EXPECT_EQ(running.size(), span_names.size()); - EXPECT_EQ(completed.size(), 0); - - // End 4th span - span_vars[3]->End(); - UpdateSpans(shared_data, completed, running, true); - - EXPECT_TRUE(ContainsNames(span_names, running, 0, 3)); // s0 s2 s1 - EXPECT_TRUE(ContainsNames(span_names, running, 4)); // + s - EXPECT_TRUE(ContainsNames(span_names, completed, 3, 4, true)); // s1 - EXPECT_EQ(running.size(), 4); - EXPECT_EQ(completed.size(), 1); - - // End 2nd and 3rd span - span_vars[1]->End(); - span_vars[2]->End(); - UpdateSpans(shared_data, completed, running, true); - - EXPECT_TRUE(ContainsNames(span_names, running, 0, 1)); // s0 - EXPECT_TRUE(ContainsNames(span_names, running, 4)); // + s - EXPECT_TRUE(ContainsNames(span_names, completed, 1, 3, true)); // s2 s1 - EXPECT_EQ(running.size(), 2); - EXPECT_EQ(completed.size(), 2); - - // End remaining Spans - span_vars[0]->End(); - span_vars[4]->End(); - UpdateSpans(shared_data, completed, running, true); - - EXPECT_TRUE(ContainsNames(span_names, completed, 0, 1)); // s0 - EXPECT_TRUE(ContainsNames(span_names, completed, 4)); // s - EXPECT_EQ(running.size(), 0); - EXPECT_EQ(completed.size(), 2); - - UpdateSpans(shared_data, completed, running, true); - - EXPECT_EQ(running.size(), 0); - EXPECT_EQ(completed.size(), 0); -} - -/* - * Test if multiple spans move from running to completed at expected times, - * running/completed spans are split. Ensure correct behavior even when - * multiple spans are discarded, even when span starting and ending is - * non-sequential. Only new completed spans are stored. - */ -TEST_F(TracezProcessor, MultipleSpansOuterSplitNewOnly) -{ - for (const auto &name : span_names) - span_vars.push_back(tracer->StartSpan(name)); - - // End last span - span_vars[4]->End(); - UpdateSpans(shared_data, completed, running, true); - - EXPECT_TRUE(ContainsNames(span_names, running, 0, 4, true)); // s0 s2 s1 s1 - EXPECT_TRUE(ContainsNames(span_names, completed, 4, 5, true)); // s - EXPECT_EQ(running.size(), 4); - EXPECT_EQ(completed.size(), 1); - - // End first span - span_vars[0]->End(); - UpdateSpans(shared_data, completed, running, true); - - EXPECT_TRUE(ContainsNames(span_names, running, 1, 4, true)); // s2 s1 s1 - EXPECT_TRUE(ContainsNames(span_names, completed, 0, 1, true)); // s0 - EXPECT_EQ(running.size(), 3); - EXPECT_EQ(completed.size(), 1); - - // End remaining middle spans - for (int i = 1; i < 4; i++) - span_vars[i]->End(); - UpdateSpans(shared_data, completed, running, true); - - EXPECT_TRUE(ContainsNames(span_names, completed, 1, 4, true)); // s2 s1 s1 - EXPECT_EQ(running.size(), 0); - EXPECT_EQ(completed.size(), 3); - - UpdateSpans(shared_data, completed, running, true); - - EXPECT_EQ(running.size(), 0); - EXPECT_EQ(completed.size(), 0); -} - -/* - * Test for ForceFlush and Shutdown code coverage, which do nothing. - */ -TEST_F(TracezProcessor, FlushShutdown) -{ - auto pre_running_sz = running.size(); - auto pre_completed_sz = completed.size(); - - EXPECT_TRUE(processor->ForceFlush()); - EXPECT_TRUE(processor->Shutdown()); - - UpdateSpans(shared_data, completed, running); - - EXPECT_EQ(pre_running_sz, running.size()); - EXPECT_EQ(pre_completed_sz, completed.size()); -} - -/* - * Test for thread safety when many spans start at the same time. - */ -TEST_F(TracezProcessor, RunningThreadSafety) -{ - std::vector> spans1; - std::vector> spans2; - - std::thread start1(StartManySpans, std::ref(spans1), tracer, 500); - std::thread start2(StartManySpans, std::ref(spans2), tracer, 500); - - start1.join(); - start2.join(); - - EndAllSpans(spans1); - EndAllSpans(spans2); -} - -/* - * Test for thread safety when many spans end at the same time - */ -TEST_F(TracezProcessor, CompletedThreadSafety) -{ - std::vector> spans1; - std::vector> spans2; - - StartManySpans(spans1, tracer, 500); - StartManySpans(spans2, tracer, 500); - - std::thread end1(EndAllSpans, std::ref(spans1)); - std::thread end2(EndAllSpans, std::ref(spans2)); - - end1.join(); - end2.join(); -} - -/* - * Test for thread safety when many snapshots are grabbed at the same time. - */ -TEST_F(TracezProcessor, SnapshotThreadSafety) -{ - std::vector> spans; - - std::thread snap1(GetManySnapshots, std::ref(shared_data), 500); - std::thread snap2(GetManySnapshots, std::ref(shared_data), 500); - - snap1.join(); - snap2.join(); - - StartManySpans(spans, tracer, 500); - - std::thread snap3(GetManySnapshots, std::ref(shared_data), 500); - std::thread snap4(GetManySnapshots, std::ref(shared_data), 500); - - snap3.join(); - snap4.join(); - - EndAllSpans(spans); -} - -/* - * Test for thread safety when many spans start while others are ending. - */ -TEST_F(TracezProcessor, RunningCompletedThreadSafety) -{ - std::vector> spans1; - std::vector> spans2; - - StartManySpans(spans1, tracer, 500); - - std::thread start(StartManySpans, std::ref(spans2), tracer, 500); - std::thread end(EndAllSpans, std::ref(spans1)); - - start.join(); - end.join(); - - EndAllSpans(spans2); -} - -/* - * Test for thread safety when many span start while snapshots are being grabbed. - */ -TEST_F(TracezProcessor, RunningSnapshotThreadSafety) -{ - std::vector> spans; - - std::thread start(StartManySpans, std::ref(spans), tracer, 500); - std::thread snapshots(GetManySnapshots, std::ref(shared_data), 500); - - start.join(); - snapshots.join(); - - EndAllSpans(spans); -} - -/* - * Test for thread safety when many spans end while snapshots are being grabbed. - */ -TEST_F(TracezProcessor, SnapshotCompletedThreadSafety) -{ - std::vector> spans; - - StartManySpans(spans, tracer, 500); - - std::thread snapshots(GetManySnapshots, std::ref(shared_data), 500); - std::thread end(EndAllSpans, std::ref(spans)); - - snapshots.join(); - end.join(); -} - -/* - * Test for thread safety when many spans start and end while snapshots are being grabbed. - */ -TEST_F(TracezProcessor, RunningSnapshotCompletedThreadSafety) -{ - std::vector> spans1; - std::vector> spans2; - - StartManySpans(spans1, tracer, 500); - - std::thread start(StartManySpans, std::ref(spans2), tracer, 500); - std::thread snapshots(GetManySnapshots, std::ref(shared_data), 500); - std::thread end(EndAllSpans, std::ref(spans1)); - - start.join(); - snapshots.join(); - end.join(); - - EndAllSpans(spans2); -}