Skip to content

Commit

Permalink
Configure tls and add example for grpcio (#450)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaysonsantos authored Feb 13, 2021
1 parent 8e0857d commit 2269d1f
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 3 deletions.
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,25 @@ jobs:
args: --all -- --check
- name: Lint
run: ./scripts/lint.sh
non-default-examples:
strategy:
matrix:
example: [examples/external-otlp-grpcio-async-std]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
with:
submodules: true
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: rustfmt
profile: minimal
- uses: arduino/setup-protoc@v1
- name: Build
run: |
cd ${{ matrix.example }}
cargo build --verbose
msrv:
runs-on: ubuntu-latest
steps:
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ members = [
"examples/tracing-grpc",
"examples/zipkin",
]
exclude = ["examples/external-otlp-grpcio-async-std"]
19 changes: 19 additions & 0 deletions examples/external-otlp-grpcio-async-std/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "external-otlp-grpcio-async-std"
version = "0.1.0"
edition = "2018"

[dependencies]
async-std = { version = "1.9.0", features = ["attributes"] }
env_logger = "0.8.2"
futures = "0.3"
opentelemetry = { path = "../../opentelemetry", features = [
"async-std",
"serialize"
] }
opentelemetry-otlp = { path = "../../opentelemetry-otlp", features = [
"grpc-sys",
"openssl-vendored"
], default-features = false }
serde_json = "1.0"
url = "2.2.0"
19 changes: 19 additions & 0 deletions examples/external-otlp-grpcio-async-std/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# External OTLP collector with grpcio and async-std with TLS

This example shows basic span, and exports to OTLP enabled collectors, like honeycomb, lightstep and other services.
Use this service in case you don't use `tokio`s runtime, for example with web frameworks like `tide` or any `async-std` library that
makes you use it as a runtime.
As these services all reside outside your own infrastructure, they require TLS for encryption to ensure your data safety.
With this example, you can export to any service that supports OTLP by using environment variables.
The following example exports data to Honeycomb:

```shell
cd examples/external-otlp-grpcio-async-std/
OTLP_GRPCIO_ENDPOINT=https://api.honeycomb.io:443 \
OTLP_GRPCIO_X_HONEYCOMB_TEAM=token \
OTLP_GRPCIO_X_HONEYCOMB_DATASET=dataset \
cargo run
```

The only required variable is `OTLP_GRPCIO_ENDPOINT` and any other variable that beggins with the prefix `OTLP_GRPCIO_` will be sent as headers
e.g.: `OTLP_GRPCIO_X_HONEYCOMB_TEAM` becomes `x-honeycomb-team` and `OTLP_GRPCIO_X_HONEYCOMB_DATASET` becomes `x-honeycomb-dataset`.
102 changes: 102 additions & 0 deletions examples/external-otlp-grpcio-async-std/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//! This should show how to connect to a third party collector like
//! honeycomb or lightstep using tonic with tls and using tokio as reactor.
//! To run this you have to specify a few environment variables like in the example:
//! ```shell
//! OTLP_GRPCIO_ENDPOINT=https://api.honeycomb.io:443 \
//! OTLP_GRPCIO_X_HONEYCOMB_TEAM=token \
//! OTLP_GRPCIO_X_HONEYCOMB_DATASET=dataset \
//! cargo run --bin external-otlp-tonic-tokio
//! ```
use async_std::task::sleep;
use opentelemetry::trace::TraceError;
use opentelemetry::{global, sdk::trace as sdktrace};
use opentelemetry::{
trace::{TraceContextExt, Tracer},
Key,
};
use url::Url;

use std::{
collections::HashMap,
env::{set_var, vars},
time::Duration,
};
use std::{
env::{remove_var, var},
error::Error,
};

// Use the variables to try and export the example to any external collector that accepts otlp
// like: oltp itself, honeycomb or lightstep
const ENDPOINT: &str = "OTLP_GRPCIO_ENDPOINT";
const HEADER_PREFIX: &str = "OTLP_GRPCIO_";

fn init_tracer() -> Result<(sdktrace::Tracer, opentelemetry_otlp::Uninstall), TraceError> {
let endpoint = var(ENDPOINT).unwrap_or_else(|_| {
panic!(
"You must specify and endpoint to connect to with the variable {:?}.",
ENDPOINT
)
});
let endpoint = Url::parse(&endpoint).expect("endpoint is not a valid url");

remove_var(ENDPOINT);
let headers: HashMap<_, _> = vars()
.filter(|(name, _)| name.starts_with(HEADER_PREFIX))
.map(|(name, value)| {
let header_name = name
.strip_prefix(HEADER_PREFIX)
.unwrap()
.replace("_", "-")
.to_ascii_lowercase();
(header_name, value)
})
.collect();

let grpcio_endpoint = format!(
"{}:{}",
endpoint.host_str().unwrap(),
endpoint.port_or_known_default().unwrap()
);

opentelemetry_otlp::new_pipeline()
.with_endpoint(grpcio_endpoint)
.with_headers(headers)
.with_tls(true)
.install()
}
const LEMONS_KEY: Key = Key::from_static_str("ex.com/lemons");
const ANOTHER_KEY: Key = Key::from_static_str("ex.com/another");

#[async_std::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
match var("RUST_LOG") {
Err(std::env::VarError::NotPresent) => set_var("RUST_LOG", "trace"),
_ => {}
};
env_logger::init();
let _guard = init_tracer()?;

let tracer = global::tracer("ex.com/basic");

tracer.in_span("operation", |cx| {
let span = cx.span();
span.add_event(
"Nice operation!".to_string(),
vec![Key::new("bogons").i64(100)],
);
span.set_attribute(ANOTHER_KEY.string("yes"));

tracer.in_span("Sub operation...", |cx| {
let span = cx.span();
span.set_attribute(LEMONS_KEY.string("five"));

span.add_event("Sub span event".to_string(), vec![]);
});
});

// wait for 1 minutes so that we could see metrics being pushed via OTLP every 10 seconds.
sleep(Duration::from_secs(60)).await;

Ok(())
}
7 changes: 7 additions & 0 deletions opentelemetry-otlp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,13 @@ impl OtlpPipelineBuilder {
self
}

/// Enable TLS without any certificate pinning.
#[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
pub fn with_tls(mut self, use_tls: bool) -> Self {
self.exporter_config.use_tls = Some(use_tls);
self
}

/// Set the timeout to the collector.
pub fn with_timeout(mut self, timeout: Duration) -> Self {
self.exporter_config.timeout = timeout;
Expand Down
15 changes: 12 additions & 3 deletions opentelemetry-otlp/src/span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ pub struct ExporterConfig {
#[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
pub compression: Option<Compression>,

/// Use TLS without any specific certificate pinning.
#[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
pub use_tls: Option<bool>,

/// The timeout to the collector.
pub timeout: Duration,

Expand Down Expand Up @@ -159,6 +163,7 @@ impl Default for ExporterConfig {
credentials: None,
headers: None,
compression: None,
use_tls: None,
timeout: Duration::from_secs(60),
completion_queue_count: 2,
}
Expand Down Expand Up @@ -287,9 +292,13 @@ impl TraceExporter {
builder = builder.default_compression_algorithm(compression.into());
}

let channel: Channel = match config.credentials {
None => builder.connect(config.endpoint.as_str()),
Some(credentials) => builder.secure_connect(
let channel: Channel = match (config.credentials, config.use_tls) {
(None, Some(true)) => builder.secure_connect(
config.endpoint.as_str(),
ChannelCredentialsBuilder::new().build(),
),
(None, _) => builder.connect(config.endpoint.as_str()),
(Some(credentials), _) => builder.secure_connect(
config.endpoint.as_str(),
ChannelCredentialsBuilder::new()
.cert(credentials.cert.into(), credentials.key.into())
Expand Down

0 comments on commit 2269d1f

Please sign in to comment.