Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Complete the initial implementation of OpenTelemetry #3677

Merged
merged 28 commits into from
May 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
6a34583
Added telemetry support for HTTP pipeline elements
LarryOsterman May 25, 2022
6c71627
Finish OpenTelemetry implementation
LarryOsterman May 25, 2022
863227a
clang-format and added doxygen comments
LarryOsterman May 25, 2022
77a1ad6
Noise reduction in diff
LarryOsterman May 25, 2022
0021685
cspell
LarryOsterman May 25, 2022
52c4de1
#include list
LarryOsterman May 25, 2022
a26e87f
Signed/unsigned issue
LarryOsterman May 25, 2022
b55e04e
Added printfs to try to track down ci failure
LarryOsterman May 25, 2022
228ac73
More traces
LarryOsterman May 26, 2022
4661f41
Fixed tracing tests in Release builds (ODR violation)
LarryOsterman May 26, 2022
30519e8
clang-format
LarryOsterman May 26, 2022
e7d4fa5
Updated input sanitizer
LarryOsterman May 27, 2022
b73b110
clang-format
LarryOsterman May 27, 2022
85f01c0
Fixed unit test; added documentation on header propagation.
LarryOsterman May 27, 2022
48abd74
More distributed tracing documentation
LarryOsterman May 27, 2022
72550ee
Fixed some typos
LarryOsterman May 27, 2022
d3f4964
clang-format and fixed clang warning
LarryOsterman May 27, 2022
799226f
Added opentelemetry to cspell.json
LarryOsterman May 27, 2022
d1a3419
cspell
LarryOsterman May 27, 2022
696834a
detail->details
LarryOsterman May 27, 2022
3dc5c28
Removed constexpr from redacted constant
LarryOsterman May 27, 2022
2201f65
CSpell
LarryOsterman May 27, 2022
9c161fd
clang-format
LarryOsterman May 27, 2022
ddedc06
clang-format
LarryOsterman May 27, 2022
4565386
Static tests need MSVC_USE_STATIC_CRT
LarryOsterman May 27, 2022
aa395e4
Missed setting static CRT in one place
LarryOsterman May 27, 2022
c7d5226
Don't run otel tests and json tests in the same test stage
LarryOsterman May 27, 2022
739903f
Moved opentelemetry code coverage under core
LarryOsterman May 28, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .vscode/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
"northcentralus",
"NTSTATUS",
"okhttp",
"opentelemetry",
"otel",
"PBYTE",
"pdbs",
Expand Down
250 changes: 250 additions & 0 deletions doc/DistributedTracing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
---
# cspell:words openetelemetry
---
# Distributed Tracing in the C++ SDK

Azure has adopted [W3C Distributed Tracing](https://www.w3.org/TR/trace-context/) as a paradigm for correlating
requests from clients across multiple services.

This document explains how the Azure C++ SDK implements distributed tracing, how clients integrate with distributed tracing, how
services should integrate with distributed tracing and finally how the network pipeline and other functionality should
integrate with distributed tracing.

## Tracing Overview

The Azure SDK for C++ Tracing APIs are modeled after the opentelemetry-cpp API surface defined in the [OpenTelemetry Tracing Specification](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md).
Additional architectural information about OpenTelemetry can be found in [OpenTelemetry Concepts](https://opentelemetry.io/docs/concepts/).

There are three major components which the Azure SDK components interact with:

- `TracerProvider` - this is a factory which creates `Tracer` objects.
- `Tracer` - this is a factory which creates `Span` objects.
- `Span` - Span objects are the APIs which allow tracing an operation.
Each `span` has a name, a type and a "status". `Spans` also contain "attributes" and "events" which describe an operation.

There is typically a single `TracerProvider` for each application, and for the Azure SDK, each
service will have a `Tracer` implementation which creates `Span` objects for each service client.

A `Span` can be considered a "unit of work" for a service. Each service method (method which calls into the service) will have a single `Span` reflecting the client method which
was called.

`Span`'s are hierarchical and each span can have multiple children (each `Span` can only have a single parent). The typical way that this manifests itself during a
service method call is:

- Service Method "MyServiceMethod" creates a span named "MyServiceMethod" and starts an HTTP request to communicate with the service.
- The HTTP pipeline (specifically the `RequestActivityPolicy`) will create a child `span` under the service method `span` named `"HTTP <verb> #0"`. This span

reflects the HTTP call into the service.
- If the HTTP call needs to be retried, the existing `span` will be closed an a new span named `HTTP <verb> #1` will be created for the retry.

## Distributed Tracing Client Integration

Applications which wish to integrate Distributed Tracing are strongly encouraged
to use the [opentelemetry-cpp](https://github.com/open-telemetry/opentelemetry-cpp) vcpkg package.

There are numerous examples on the OpenTelemetry web site which demonstrate how to integrate
opentelemetry into a customer application and integrate the generated traces
with Azure monitoring infrastructure such as Geneva Monitoring.

Following the examples from opentelemetry-cpp, the following can be used
to establish an OpenTelemetry exporter which logs to the console or to an
in-memory logger.

```c++
opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider>
CreateOpenTelemetryProvider()
{
#if USE_MEMORY_EXPORTER
auto exporter = std::make_unique<opentelemetry::exporter::memory::InMemorySpanExporter>();
#else
auto exporter = std::make_unique<opentelemetry::exporter::trace::OStreamSpanExporter>();
#endif

// simple processor
auto simple_processor = std::unique_ptr<opentelemetry::sdk::trace::SpanProcessor>(
new opentelemetry::sdk::trace::SimpleSpanProcessor(std::move(exporter)));

auto always_on_sampler = std::unique_ptr<opentelemetry::sdk::trace::AlwaysOnSampler>(
new opentelemetry::sdk::trace::AlwaysOnSampler);

auto resource_attributes = opentelemetry::sdk::resource::ResourceAttributes{
{"service.name", "telemetryTest"}, {"service.instance.id", "instance-1"}};
auto resource = opentelemetry::sdk::resource::Resource::Create(resource_attributes);
// Create using SDK configurations as parameter
return opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider>(
new opentelemetry::sdk::trace::TracerProvider(
std::move(simple_processor), resource, std::move(always_on_sampler)));
}
```

Other exporters exist to export to [Jaeger](https://github.com/open-telemetry/opentelemetry-cpp/tree/main/exporters/jaeger),
[Windows ETW](https://github.com/open-telemetry/opentelemetry-cpp/tree/main/exporters/etw) and others.

Once the `opentelemetry::trace::TracerProvider` has been created, The client needs to create a new `Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider` which
functions as an abstract class integration between OpenTelemetry and Azure Core:

```c++
std::shared_ptr<Azure::Core::Tracing::TracerProvider> traceProvider
= std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(CreateOpenTelemetryProvider());
```

To finish the integration with Azure clients, there are two mechanisms to integrate OpenTelemetry into a client application:

1) `Azure::Core::Context` integration.
1) Service Client Options integration.

### Integrate an OpenTelemetryProvider via the ApplicationContext

To integrate OpenTelemetry for all Azure Clients in the application, the customer can call `Azure::Core::Context::ApplicationContext.SetTracerProvider` to establish the
tracer provider for the application.

```c++
Azure::Core::Context::ApplicationContext.SetTracerProvider(provider);
```

### Integrate an OpenTelemetryProvider via Service ClientOptions

While using the ApplicationContext is the simplest mechanism for integration OpenTelemetry with a customer application, there may be times the customer needs more flexibility when creating service clients.
To enable customers to further customize how tracing works, the application can set the `Telemetry.TracingProvider` field in the service client options, which will establish the tracer provider used by
the service client.

```c++
auto tracerProvider(CreateOpenTelemetryProvider());
auto provider(std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(tracerProvider));

ServiceClientOptions clientOptions;
clientOptions.Telemetry.TracingProvider = provider;
clientOptions.Telemetry.ApplicationId = "MyApplication";
ServiceClient myServiceClient(clientOptions);
```
## Distributed Tracing Service Integration
There are two steps needed to integrate Distributed Tracing with a Service Client.
1. Add a `DiagnosticTracingFactory` object to the ServiceClient object
1. Update each service method as follows:
1. Add a call to the `CreateSpan` method on the diagnostic tracing factory. This will create a new span for the client operation.
1. Call `SetStatus` on the created span when the service method successfully completes.
1. Wrap the client method code with a try/catch handler which catches exceptions and call AddEvent with the value of the exception.
### Add a `DiagnosticTracingFactory` to the serviceClient class
To add a new `DiagnosticTracingFactory` to the client, simply add the class as a member:
```c++
Azure::Core::Tracing::_internal::DiagnosticTracingFactory m_tracingFactory;
```

And construct the new tracing factory in the service constructor:

```c++
explicit ServiceClient(ServiceClientOptions const& clientOptions = ServiceClientOptions{})
: m_tracingFactory(clientOptions, "Azure.Core.OpenTelemetry.Test.Service", PackageVersion::ToString())
```
### Update Each Service Method
There are three methods of interest when updating the service method:
1. `DiagnosticTracingFactory::CreateSpan` - this creates and returns a `Span` and `Context` object for the service method. The returned Context object must be used for subsequent service operations.
1. `Span::AddEvent(std::exception&)` - This registers the exception with the distributed tracing infrastructure.
1. `Span::SetStatus` - This sets the status of the operation in the trace.
```c++
Azure::Response<std::string> ServiceMethod(
std::string const&,
Azure::Core::Context const& context = Azure::Core::Context{})
{
// Create a new context and span for this request.
auto contextAndSpan = m_tracingFactory.CreateSpan(
"ServiceMethod", Azure::Core::Tracing::_internal::SpanKind::Internal, context);
// contextAndSpan.first is the new context for the operation.
// contextAndSpan.second is the new span for the operation.
try
{
// <Call Into Service via an HTTP pipeline>
Azure::Core::Http::Request requestToSend(
HttpMethod::Get, Azure::Core::Url("<Service URL>"));
std::unique_ptr<Azure::Core::Http::RawResponse> response
= m_pipeline->Send(requestToSend, contextAndSpan.first);
contextAndSpan.second.SetStatus(Azure::Core::Tracing::_internal::SpanStatus::Ok);
return Azure::Response<std::string>("", std::move(response));
}
catch (std::exception const& ex)
{
// Register that the exception has happened and that the span is now in error.
contextAndSpan.second.AddEvent(ex);
contextAndSpan.second.SetStatus(Azure::Core::Tracing::_internal::SpanStatus::Error);
throw;
}
// When contextAndSpan.second goes out of scope, it ends the span, which will record it.
}
};
```

## Implementation Details

### Distributed Tracing components

In order to maintain flexibility, the opentelemetry-cpp APIs are implemented in a separate package - azure-core-tracing-opentelemetry.
This is consistent with how opentelemetry is distributed for
the other Azure SDKs.

The Azure Core API surface interacts with a set of pure virtual base classes (aka "interfaces") in
the `Azure::Core::Tracing` and `Azure::Core::Tracing::_internal` namespace. These allow a level of separation
between the Azure Core API surface and the OpenTelemetry API surface - an alternative tracing mechanism needs
to provide APIs consistent with the `Azure::Core::Tracing` APIs.

The azure-core-tracing-openetelemetry-cpp package implements a set of APIs in the `Azure::Core::Tracing::OpenTelemetry`
and `Azure::Core::Tracing::OpenTelemetry::_detail` namespace. These provide an Azure Core compatable API surface for distributed tracing.

The core service client interface is the `DiagnosticTracingFactory` class which implements two APIs: `CreateSpan` and
`CreateSpanFromContext`. `CreateSpan` is intended to be used by service methods which have direct access to a
`DiagnosticTracingFactory` object, `CreateSpanFromContext` in intended to be used from code which does NOT have
direct access to the `DiagnosticTracingFactory`.

The final significant piece of the distributed tracing infrastructure is the `RequestActivityPolicy` - this policy MUST be
inserted into the HTTP pipeline AFTER the `RetryPolicy`. It is responsible for creating the span associated with the HTTP request, it will
also propagate the W3C distributed tracing headers from the span into the HTTP request.

### Generated traces

The Azure standards for distributed tracing are define in [Azure Distributed Tracing Conventions](https://github.com/Azure/azure-sdk/blob/main/docs/tracing/distributed-tracing-conventions.md).
The actual tracing elements generated by Azure services are defined in [Azure Tracing Conventions YAML](https://github.com/Azure/azure-sdk/blob/main/docs/tracing/distributed-tracing-conventions.yml).

In summary, these are the traces and attributes which should be generated
for azure services:

#### Spans

The distributed tracing standards define the following traces:

##### Public APIs

All public APIs MUST create a span which will describes the API.
The name of the span MUST be the API name.

##### HTTP Calls

Each HTTP request sent to the service MUST create a span describing the request to the service.
The name of the span MUST be of the form `HTTP <HTTP VERB> #<HTTP RETRY>`.

#### Attributes

Generated traces have the following attributes:

| Attribute Name | Semantics | Where Used
|-----------|--------|-------
| `az.namespace` |Namespace of the azure service request| All spans.
| `http.method`| HTTP Method ("GET", "PUT", etc)| HTTP Spans.
| `http.url`| URL being retrieved (sanitized)| HTTP Spans.
| `http.status_code` | HTTP status code returned by the service | HTTP Spans.
| `http.user_agent` | The value of the `User-Agent` HTTP header sent to the service | HTTP Spans.
| `requestId` | The value of the `x-ms-client-request-id` header sent by the client | HTTP Spans.
| `serviceRequestId` | The value -f the `x-ms-request-id` sent by the server | HTTP Spans.
37 changes: 27 additions & 10 deletions eng/pipelines/templates/stages/platform-matrix-live.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,56 +73,73 @@
"Win_x86_with_unit_test_winHttp": {
"VcpkgInstall": "openssl",
"CMAKE_GENERATOR_PLATFORM": "Win32",
"CmakeArgs": " -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_PERFORMANCE_TESTS=ON ",
"CmakeArgs": " -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_PERFORMANCE_TESTS=ON -DMSVC_USE_STATIC_CRT=ON ",
"VCPKG_DEFAULT_TRIPLET": "x86-windows-static",
"WindowsCtestConfig": "-C Release",
"BuildArgs": "-v --parallel 8 --config Release"
},
"Win_x86_no_rtti_whit_unit_test": {
"Win_x86_no_rtti_with_unit_test": {
"VcpkgInstall": "libxml2 openssl",
"CMAKE_GENERATOR_PLATFORM": "Win32",
"CmakeArgs": " -DBUILD_RTTI=OFF -DBUILD_TESTING=ON -DBUILD_SAMPLES=ON",
"CmakeArgs": " -DBUILD_RTTI=OFF -DBUILD_TESTING=ON -DBUILD_SAMPLES=ON -DMSVC_USE_STATIC_CRT=ON",
"VCPKG_DEFAULT_TRIPLET": "x86-windows-static",
"WindowsCtestConfig": "-C Release",
"BuildArgs": "-v --parallel 8 --config Release"
},
"Win_x86_with_unit_test_libcurl": {
"CMAKE_GENERATOR_PLATFORM": "Win32",
"VCPKG_DEFAULT_TRIPLET": "x86-windows-static",
"CmakeArgs": " -DBUILD_TRANSPORT_CURL=ON -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_PERFORMANCE_TESTS=ON ",
"CmakeArgs": " -DBUILD_TRANSPORT_CURL=ON -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_PERFORMANCE_TESTS=ON -DMSVC_USE_STATIC_CRT=ON ",
"BuildArgs": "-v --parallel 8"
},
"Win_x64_with_unit_test_winHttp": {
"Win_x64_with_json_unit_test_winHttp": {
"VcpkgInstall": "openssl",
"CMAKE_GENERATOR_PLATFORM": "x64",
"CmakeArgs": " -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_PERFORMANCE_TESTS=ON ",
"CmakeArgs": " -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_PERFORMANCE_TESTS=ON -DDISABLE_AZURE_CORE_OPENTELEMETRY=ON ",
"BuildArgs": "-v --parallel 8 --config Release",
"AZURE_CORE_ENABLE_JSON_TESTS": 1,
"VCPKG_DEFAULT_TRIPLET": "x64-windows-static",
"WindowsCtestConfig": "-C Release"
},
"Win_x64_with_unit_samples_winHttp": {
"Win_x64_with_json_unit_samples_winHttp": {
"VcpkgInstall": "openssl",
"VCPKG_DEFAULT_TRIPLET": "x64-windows-static",
"CMAKE_GENERATOR_PLATFORM": "x64",
"CmakeArgs": " -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_PERFORMANCE_TESTS=ON -DBUILD_SAMPLES=ON ",
"CmakeArgs": " -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_PERFORMANCE_TESTS=ON -DBUILD_SAMPLES=ON -DDISABLE_AZURE_CORE_OPENTELEMETRY=ON ",
"BuildArgs": "-v --parallel 8 --config Release",
"AZURE_CORE_ENABLE_JSON_TESTS": 1,
"RunSamples": 1,
"WindowsCtestConfig": "-C Release"
},
"Win_x64_with_unit_test_winHttp": {
"VcpkgInstall": "openssl",
"CMAKE_GENERATOR_PLATFORM": "x64",
"CmakeArgs": " -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_PERFORMANCE_TESTS=ON -DMSVC_USE_STATIC_CRT=ON ",
"BuildArgs": "-v --parallel 8 --config Release",
"VCPKG_DEFAULT_TRIPLET": "x64-windows-static",
"WindowsCtestConfig": "-C Release"
},
"Win_x64_with_unit_samples_winHttp": {
"VcpkgInstall": "openssl",
"VCPKG_DEFAULT_TRIPLET": "x64-windows-static",
"CMAKE_GENERATOR_PLATFORM": "x64",
"CmakeArgs": " -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_PERFORMANCE_TESTS=ON -DBUILD_SAMPLES=ON -DMSVC_USE_STATIC_CRT=ON ",
"BuildArgs": "-v --parallel 8 --config Release",
"RunSamples": 1,
"WindowsCtestConfig": "-C Release"
},
"Win_x64_with_unit_test_libcurl": {
"VCPKG_DEFAULT_TRIPLET": "x64-windows-static",
"CMAKE_GENERATOR_PLATFORM": "x64",
"CmakeArgs": " -DBUILD_TRANSPORT_CURL=ON -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_PERFORMANCE_TESTS=ON ",
"CmakeArgs": " -DBUILD_TRANSPORT_CURL=ON -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_PERFORMANCE_TESTS=ON -DMSVC_USE_STATIC_CRT=ON ",
"BuildArgs": "-v --parallel 8 --config Release",
"WindowsCtestConfig": "-C Release"
},
"Win_x64_with_unit_samples_libcurl": {
"VcpkgInstall": "curl[winssl] openssl",
"VCPKG_DEFAULT_TRIPLET": "x64-windows-static",
"CMAKE_GENERATOR_PLATFORM": "x64",
"CmakeArgs": " -DBUILD_TRANSPORT_CURL=ON -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_PERFORMANCE_TESTS=ON -DBUILD_SAMPLES=ON ",
"CmakeArgs": " -DBUILD_TRANSPORT_CURL=ON -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_PERFORMANCE_TESTS=ON -DBUILD_SAMPLES=ON -DMSVC_USE_STATIC_CRT=ON ",
"BuildArgs": "-v --parallel 8 --config Release",
"RunSamples": 1,
"WindowsCtestConfig": "-C Release"
Expand Down
4 changes: 4 additions & 0 deletions sdk/attestation/azure-security-attestation/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
---
# cspell:words opentelemetry
---

# Azure Attestation Package client library for C++

Microsoft Azure Attestation is a unified solution for remotely verifying the trustworthiness of a platform and integrity of the binaries running inside it. The service supports attestation of the platforms backed by Trusted Platform Modules (TPMs) alongside the ability to attest to the state of Trusted Execution Environments (TEEs) such as Intel(tm) Software Guard Extensions (SGX) enclaves and Virtualization-based Security (VBS) enclaves.
Expand Down
4 changes: 2 additions & 2 deletions sdk/attestation/test-resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"metadata": {
"description": "The application client secret used to run tests."
}
},
}
},
"variables": {
"isolatedTenantName": "[concat('cp', concat(parameters('baseName'), 'iso'))]",
Expand All @@ -66,7 +66,7 @@
"type": "Microsoft.Attestation/attestationProviders",
"apiVersion": "2020-10-01",
"name": "[variables('aadTenantName')]",
"location": "[parameters('location')]",
"location": "[parameters('location')]"
},
{
"type": "Microsoft.Attestation/attestationProviders",
Expand Down
2 changes: 2 additions & 0 deletions sdk/core/azure-core-tracing-opentelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@

## 1.0.0-beta.1 (Unreleased)

### Features Added

- Initial release
Loading