Skip to content

Commit 01c1840

Browse files
committed
ci: add weaver live-check workflow for SDK self-observability metrics (open-telemetry#3553)
1 parent 8b93943 commit 01c1840

3 files changed

Lines changed: 183 additions & 0 deletions

File tree

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
name: SDK Self-Observability Live Check
2+
3+
# Validates that self-observability telemetry emitted by the OpenTelemetry
4+
# SDK (metrics about its own internal state) complies with the published
5+
# semantic conventions. Tracked by:
6+
# https://github.com/open-telemetry/opentelemetry-rust/issues/3553
7+
8+
env:
9+
CI: true
10+
WEAVER_VERSION: v0.24.0
11+
# Pin to a release that contains the otel.sdk.* metric definitions.
12+
SEMCONV_REGISTRY: 'https://github.com/open-telemetry/semantic-conventions/archive/refs/tags/v1.42.0.tar.gz[model]'
13+
14+
permissions:
15+
contents: read
16+
17+
on:
18+
workflow_dispatch:
19+
pull_request:
20+
paths:
21+
- 'opentelemetry-sdk/**'
22+
- 'opentelemetry-otlp/**'
23+
- 'examples/self-obs-live-check/**'
24+
- '.github/workflows/sdk-self-observability.yml'
25+
merge_group:
26+
push:
27+
branches:
28+
- main
29+
paths:
30+
- 'opentelemetry-sdk/**'
31+
- 'opentelemetry-otlp/**'
32+
- 'examples/self-obs-live-check/**'
33+
- '.github/workflows/sdk-self-observability.yml'
34+
35+
concurrency:
36+
group: ${{ github.workflow }}-${{ github.ref }}
37+
cancel-in-progress: true
38+
39+
jobs:
40+
live-check:
41+
runs-on: ubuntu-latest
42+
timeout-minutes: 15
43+
steps:
44+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
45+
46+
- name: Install stable Rust
47+
run: rustup toolchain install stable --profile minimal --no-self-update
48+
49+
- name: Cache cargo
50+
uses: actions/cache@v4
51+
with:
52+
path: |
53+
~/.cargo/registry
54+
~/.cargo/git
55+
target
56+
key: self-obs-live-check-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
57+
58+
- name: Build live-check app
59+
run: cargo build --release -p self-obs-live-check
60+
61+
- name: Setup weaver
62+
uses: open-telemetry/weaver/.github/actions/setup-weaver@v0.24.0
63+
with:
64+
version: ${{ env.WEAVER_VERSION }}
65+
66+
- name: Start weaver live-check
67+
id: live-check
68+
uses: open-telemetry/weaver/.github/actions/weaver-live-check-start@v0.24.0
69+
with:
70+
registry: ${{ env.SEMCONV_REGISTRY }}
71+
72+
- name: Run live-check app
73+
env:
74+
OTEL_METRIC_EXPORT_INTERVAL: '2000'
75+
OTEL_BSP_SCHEDULE_DELAY: '1000'
76+
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ steps.live-check.outputs.otlp-grpc-endpoint }}
77+
run: |
78+
set -euo pipefail
79+
./target/release/self-obs-live-check > "${{ steps.live-check.outputs.state-dir }}/app.log" 2>&1
80+
echo "App exited cleanly"
81+
82+
- name: Stop weaver live-check
83+
if: always()
84+
uses: open-telemetry/weaver/.github/actions/weaver-live-check-stop@v0.24.0
85+
with:
86+
fail-on: violation
87+
88+
- name: Upload live-check report
89+
if: always()
90+
uses: actions/upload-artifact@v4
91+
with:
92+
name: sdk-self-obs-live-check
93+
path: ${{ steps.live-check.outputs.state-dir }}
94+
if-no-files-found: warn
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[package]
2+
name = "self-obs-live-check"
3+
version = "0.1.0"
4+
edition = "2021"
5+
license = "Apache-2.0"
6+
rust-version = "1.75.0"
7+
publish = false
8+
autobenches = false
9+
10+
# Purpose-built example for the sdk-self-observability CI workflow.
11+
# Exercises the SDK's self-observability metrics by creating a LoggerProvider
12+
# with a BatchLogProcessor + OTLP gRPC exporter, emitting logs, and shutting
13+
# down. Not intended as illustrative user-facing documentation.
14+
15+
[[bin]]
16+
name = "self-obs-live-check"
17+
path = "src/main.rs"
18+
bench = false
19+
20+
[dependencies]
21+
opentelemetry = { workspace = true }
22+
opentelemetry_sdk = { workspace = true, features = ["logs", "metrics", "experimental_metrics_bound_instruments"] }
23+
opentelemetry-otlp = { workspace = true, features = ["grpc-tonic", "logs", "metrics"] }
24+
opentelemetry-appender-tracing = { workspace = true }
25+
tracing = { workspace = true, features = ["std"] }
26+
tracing-subscriber = { workspace = true, features = ["env-filter", "registry", "std", "fmt"] }
27+
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Purpose-built binary for the sdk-self-observability CI workflow.
2+
//
3+
// Exercises the SDK's self-observability metrics by:
4+
// 1. Creating a MeterProvider (so the SDK can emit metrics about itself)
5+
// 2. Creating a LoggerProvider with a BatchLogProcessor + OTLP exporter
6+
// 3. Emitting log records (exercises otel.sdk.log.created + otel.sdk.processor.log.processed)
7+
// 4. Calling shutdown (exercises metrics flush + future shutdown event)
8+
//
9+
// The OTLP endpoint is taken from OTEL_EXPORTER_OTLP_ENDPOINT (set by CI
10+
// to point at the weaver live-check listener). Metrics and logs both go
11+
// to the same endpoint.
12+
13+
use opentelemetry::global;
14+
use opentelemetry_otlp::{LogExporter, MetricExporter};
15+
use opentelemetry_sdk::{
16+
logs::SdkLoggerProvider,
17+
metrics::{PeriodicReader, SdkMeterProvider},
18+
Resource,
19+
};
20+
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
21+
22+
const SERVICE_NAME: &str = "self-obs-live-check";
23+
24+
#[tokio::main]
25+
async fn main() {
26+
// --- MeterProvider (SDK uses this internally for self-obs metrics) ---
27+
let metric_exporter = MetricExporter::builder().with_tonic().build().unwrap();
28+
let reader = PeriodicReader::builder(metric_exporter).build();
29+
let meter_provider = SdkMeterProvider::builder()
30+
.with_reader(reader)
31+
.with_resource(Resource::builder().with_service_name(SERVICE_NAME).build())
32+
.build();
33+
global::set_meter_provider(meter_provider.clone());
34+
35+
// --- LoggerProvider with OTLP exporter (self-obs target) ---
36+
let log_exporter = LogExporter::builder().with_tonic().build().unwrap();
37+
let logger_provider = SdkLoggerProvider::builder()
38+
.with_resource(Resource::builder().with_service_name(SERVICE_NAME).build())
39+
.with_batch_exporter(log_exporter)
40+
.build();
41+
42+
// Wire tracing -> OTel logs so we can emit log records via tracing macros
43+
let otel_layer =
44+
opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge::new(&logger_provider);
45+
tracing_subscriber::registry()
46+
.with(otel_layer)
47+
.with(tracing_subscriber::fmt::layer().with_target(true))
48+
.init();
49+
50+
// --- Emit log records to exercise otel.sdk.log.created ---
51+
for i in 0..10 {
52+
tracing::info!(iteration = i, "log record {i}");
53+
}
54+
55+
// Brief pause to let the BatchLogProcessor's scheduled export fire,
56+
// exercising otel.sdk.processor.log.processed and the exporter metrics.
57+
tokio::time::sleep(std::time::Duration::from_secs(3)).await;
58+
59+
// --- Shutdown: flushes remaining records, exercises processor/exporter ---
60+
let _ = logger_provider.shutdown();
61+
let _ = meter_provider.shutdown();
62+
}

0 commit comments

Comments
 (0)