From 42168f9e6f0a64820ffb256bad0d305efbc8a9e8 Mon Sep 17 00:00:00 2001 From: Nick Hudson Date: Tue, 8 Aug 2023 10:05:18 -0500 Subject: [PATCH] Add in CI and Publish Actions, finialize initial code and examples --- .github/CODEOWNERS | 1 + .github/actions/publish-crate/action.yaml | 79 ++++++++++++++++++++ .github/workflows/release.yaml | 48 +++++++++++++ .github/workflows/rust-lint-test.yaml | 84 ++++++++++++++++++++++ .gitignore | 2 + Cargo.toml | 2 +- examples/actix-basic-http/Cargo.toml | 11 +++ examples/actix-basic-http/src/main.rs | 66 +++++++++++++++++ examples/actix-basic-http/src/telemetry.rs | 0 src/lib.rs | 6 +- 10 files changed, 295 insertions(+), 4 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/actions/publish-crate/action.yaml create mode 100644 .github/workflows/release.yaml create mode 100644 .github/workflows/rust-lint-test.yaml create mode 100644 examples/actix-basic-http/Cargo.toml create mode 100644 examples/actix-basic-http/src/main.rs create mode 100644 examples/actix-basic-http/src/telemetry.rs diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..b546077 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @ChuckHend @ianstanton @ryw @sjmiller609 @nhudson diff --git a/.github/actions/publish-crate/action.yaml b/.github/actions/publish-crate/action.yaml new file mode 100644 index 0000000..db9d822 --- /dev/null +++ b/.github/actions/publish-crate/action.yaml @@ -0,0 +1,79 @@ +name: 'Publish to crates.io' +description: 'Publish cratest to crates.io and some other crates.io-related actions, like checking if a version is already published.' +inputs: + working-directory: + required: false + description: "In which directory should we run 'cargo publish'?" + default: "." + cargo-registry-token: + required: true + description: "The CARGO_REGISTRY_TOKEN to use" + fail-if-version-published: + description: "If the version is already published, should we fail, or ignore? By default, ignore." + required: false + default: false + dry-run: + description: "Use --dry-run flag on cargo publish?" + required: false + default: false + toolchain: + description: "Which Rust toolchain to use?" + default: "stable" + required: false +outputs: {} +runs: + using: "composite" + steps: + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ inputs.toolchain }} + - name: Install TOML parser + shell: bash + run: | + set -xe + wget https://github.com/freshautomations/stoml/releases/download/v0.7.1/stoml_linux_amd64 &> /dev/null + mv stoml_linux_amd64 stoml + chmod +x stoml + sudo mv stoml /usr/local/bin/ + - name: Publish + shell: bash + working-directory: ${{ inputs.working-directory }} + run: | + set -xe + + # If package.publish is false, skip the rest + SHOULD_PUBLISH=$(stoml Cargo.toml package.publish) + if [[ "${SHOULD_PUBLISH}" == "false" ]]; then + echo "Found package.publish is false. Skipping." + exit 0 + fi + + # Get crate information + NAME=$(stoml Cargo.toml package.name) + VERSION=$(stoml Cargo.toml package.version) + VERSIONS_INFO=$(curl https://crates.io/api/v1/crates/${NAME} 2> /dev/null) + # "|| true" handles the case where this crate hasn't yet been published + PUBLISHED_VERSIONS=$(echo ${VERSIONS_INFO} | jq -r '.versions[] | .num' || true) + echo ${VERSIONS_INFO} + + # If this version is already published... + if echo ${VERSIONS_INFO} | jq -r ".versions[] | .num | . == \"${VERSION}\"" | grep true; then + echo "The version '${VERSION}' of '${NAME}' is already published." + if [ "${{ inputs.fail-if-version-published }}" == "true" ]; then + exit 1 + else + echo "Skipping the rest of the action because inputs.fail-if-version-published is false." + exit 0 + fi + fi + echo "Did not detect the version ${VERSION} to be already published." + echo "The list of known versions:" + echo $PUBLISHED_VERSIONS + + # Set --dry-run flag, if configured + DRY_RUN="" + if [ "${{ inputs.dry-run }}" == "true" ]; then + DRY_RUN="--dry-run" + fi + + cargo publish ${DRY_RUN} --token ${{ inputs.cargo-registry-token }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..562a3aa --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,48 @@ +name: Release and Publish +on: + pull_request: + branches: + - main + - "release/[0-9]+.[0-9]+" + push: + branches: + - 'main' + - 'release/[0-9]+.[0-9]+' +jobs: + changelog: + runs-on: ubuntu-latest + steps: + - name: "✏️ Generate release changelog" + uses: heinrichreimer/github-changelog-generator-action@v2.3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + cargo-publish: + name: Cargo publish + runs-on: ubuntu-latest + strategy: + fail-fast: false + steps: + - name: Check out the repo + uses: actions/checkout@v3 + - name: Determine which flags to use on cargo publish + id: cargo_flags + run: | + set -x + BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) + if [ "${BRANCH_NAME}" == "main" ]; then + echo "dry_run=false" >> $GITHUB_OUTPUT + echo "fail_if_version_published=false" >> $GITHUB_OUTPUT + elif [[ "${BRANCH_NAME}" == release/* ]]; then + echo "dry_run=false" >> $GITHUB_OUTPUT + echo "fail_if_version_published=false" >> $GITHUB_OUTPUT + else + echo "dry_run=true" >> $GITHUB_OUTPUT + echo "fail_if_version_published=true" >> $GITHUB_OUTPUT + fi + - name: Publish or validate + uses: ./.github/actions/publish-crate + with: + dry-run: ${{ steps.cargo_flags.outputs.dry_run }} + fail-if-version-published: ${{ steps.cargo_flags.outputs.fail_if_version_published }} + cargo-registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/rust-lint-test.yaml b/.github/workflows/rust-lint-test.yaml new file mode 100644 index 0000000..d1cc67c --- /dev/null +++ b/.github/workflows/rust-lint-test.yaml @@ -0,0 +1,84 @@ +name: tembo-telemetry ci workflow + +defaults: + run: + shell: bash +on: + push: + branches: + - main + pull_request: + branches: + - main + +env: + # Disable incremental compilation. + # + # Incremental compilation is useful as part of an edit-build-test-edit cycle, + # as it lets the compiler avoid recompiling code that hasn't changed. However, + # on CI, we're not making small edits; we're almost always building the entire + # project from scratch. Thus, incremental compilation on CI actually + # introduces *additional* overhead to support making future builds + # faster...but no future builds will ever occur in any given CI environment. + # + # See https://matklad.github.io/2021/09/04/fast-rust-builds.html#ci-workflow + # for details. + CARGO_INCREMENTAL: 0 + # Allow more retries for network requests in cargo (downloading crates) and + # rustup (installing toolchains). This should help to reduce flaky CI failures + # from transient network timeouts or other issues. + CARGO_NET_RETRY: 10 + RUSTUP_MAX_RETRIES: 10 + # Don't emit giant backtraces in the CI logs. + RUST_BACKTRACE: short + +jobs: + check: + name: cargo check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + - name: Check + run: cargo check --all --tests --benches + + style: + name: cargo fmt + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + toolchain: nightly + components: rustfmt + - name: rustfmt + run: cargo +nightly fmt --all -- --check + + warnings: + # Check for any warnings. This is informational and thus is allowed to fail. + runs-on: ubuntu-latest + needs: check + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy + - name: Clippy + uses: actions-rs/clippy-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: --all --examples --tests --benches -- -D warnings + + tests: + name: Run tests + runs-on: ubuntu-22.04 + needs: style + steps: + - uses: actions/checkout@v2 + - name: Install Rust stable toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - name: run all tests + run: cargo test diff --git a/.gitignore b/.gitignore index 4fffb2f..044c85e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /target /Cargo.lock +examples/*/Cargo.lock +examples/*/target diff --git a/Cargo.toml b/Cargo.toml index 6b22296..d90f3bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] async-trait = "0.1" tracing = "0.1" -opentelemetry = { version = "0.19", features = ["rt-tokio", "metrics"] } +opentelemetry = { version = "0.19", features = ["rt-tokio-current-thread", "metrics"] } opentelemetry-otlp = { version = "0.12", features = ["tonic", "trace", "metrics", "tls", "tls-roots"] } tracing-bunyan-formatter = "0.3" tracing-subscriber = { version = "0.3", features = ["registry", "env-filter", "fmt"] } diff --git a/examples/actix-basic-http/Cargo.toml b/examples/actix-basic-http/Cargo.toml new file mode 100644 index 0000000..8bc5d96 --- /dev/null +++ b/examples/actix-basic-http/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "trace" +version = "0.1.0" +edition = "2021" + +[dependencies] +actix-web = "4" +tracing = "0.1" +tracing-actix-web = { version="0.7", features = ["opentelemetry_0_19"] } +opentelemetry = { version = "0.19", features = ["metrics"] } +tembo-telemetry = { version = "0.1.0", path = "../../" } diff --git a/examples/actix-basic-http/src/main.rs b/examples/actix-basic-http/src/main.rs new file mode 100644 index 0000000..7a56b2f --- /dev/null +++ b/examples/actix-basic-http/src/main.rs @@ -0,0 +1,66 @@ +use actix_web::{get, web, App, HttpResponse, HttpServer, Responder}; +use opentelemetry::global; +use std::io; +use tembo_telemetry::{TelemetryConfig, TelemetryInit}; +use tracing::*; +use tracing_actix_web::TracingLogger; + +const TRACER_NAME: &str = "tembo.io/trace"; + +#[get("/")] +#[instrument] +async fn hello(tc: web::Data) -> impl Responder { + let trace_id = tc.get_trace_id(); + Span::current().record("trace_id", &field::display(&trace_id)); + info!("Received request for /hello"); + HttpResponse::Ok().json("Hello World!") +} + +#[actix_web::main] +async fn main() -> io::Result<()> { + // Setup Telemetry and Logging + let telemetry_config = if let Ok(otlp_endpoint) = std::env::var("OPENTELEMETRY_ENDPOINT_URL") { + let tc = TelemetryConfig { + app_name: std::env::var("CARGO_BIN_NAME").unwrap_or_else(|_| "basic".to_string()), + env: std::env::var("ENV").unwrap_or_else(|_| "development".to_string()), + endpoint_url: Some(otlp_endpoint), + tracer_id: Some(TRACER_NAME.to_string()), + }; + println!("{:?}", tc); + let _telemetry = TelemetryInit::init(&tc).await; + tc + } else { + let tc = TelemetryConfig { + app_name: std::env::var("ENV").unwrap_or_else(|_| "development".to_string()), + env: std::env::var("ENV").unwrap_or_else(|_| "development".to_string()), + endpoint_url: None, + tracer_id: Some(TRACER_NAME.to_string()), + }; + let _telemetry = TelemetryInit::init(&tc).await; + tc + }; + + let server_bind_address = "0.0.0.0:3001".to_string(); + let server = HttpServer::new({ + let telemerty_config = web::Data::new(telemetry_config); + move || { + App::new() + .app_data(telemerty_config.clone()) + .service(hello) + .wrap(TracingLogger::default()) + } + }) + .bind(server_bind_address.clone())? + .shutdown_timeout(5) + .run(); + + info!( + "Starting HTTP server at https://{}/", + server_bind_address.clone() + ); + server.await?; + + global::shutdown_tracer_provider(); + + Ok(()) +} diff --git a/examples/actix-basic-http/src/telemetry.rs b/examples/actix-basic-http/src/telemetry.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/lib.rs b/src/lib.rs index 47c6fb0..4e1e686 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,8 +12,8 @@ use async_trait::async_trait; use opentelemetry::{ - global, sdk::propagation::TraceContextPropagator, sdk::trace, sdk::Resource, trace::TraceId, - KeyValue, + global, runtime::TokioCurrentThread, sdk::propagation::TraceContextPropagator, sdk::trace, + sdk::Resource, trace::TraceId, KeyValue, }; use opentelemetry_otlp::WithExportConfig; use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer}; @@ -93,7 +93,7 @@ impl TelemetryInit for TelemetryConfig { .tracing() .with_exporter(exporter) .with_trace_config(trace_config) - .install_batch(opentelemetry::runtime::Tokio)?; + .install_batch(TokioCurrentThread)?; let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); if self.env == "development" { let logger = fmt::layer().compact();