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

support tracing subscriber #8

Merged
merged 7 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 5 additions & 6 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
rust:
- stable
# the scripts seem to inevitably involve building something that requires a newer version
# or rust, so we just run against stable for now.
# of rust, so we just run against stable for now.
# - 1.60.0
steps:
# Checkout, setup Rust tools, etc.
Expand All @@ -40,7 +40,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: make
args: --makefile Makefile.toml ci-flow
args: --makefile Makefile.toml check-all

# Setup Python and run Python example
- name: Setup python
Expand All @@ -50,8 +50,7 @@ jobs:
- name: Install poetry
uses: snok/install-poetry@v1
- name: Run Python Example
uses: actions-rs/cargo@v1
with:
command: make
args: --makefile Makefile.toml example-lib-python
run: |
cd ./examples/pyo3-opentelemetry-lib
cargo make --makefile Makefile.toml python-check-all

6 changes: 3 additions & 3 deletions .github/workflows/prerelease.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ jobs:
with:
fetch-depth: 0
token: ${{ secrets.PAT }}
- uses: actions-rs/toolchain@v1
- name: Install Knope
uses: knope-dev/action@v2.0.0
with:
toolchain: stable
- run: cargo install knope --version 0.13.2
version: 0.13.2
- run: |
git config --global user.name "${{ github.triggering_actor }}"
git config --global user.email "${{ github.triggering_actor}}@users.noreply.github.com"
Expand Down
40 changes: 27 additions & 13 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,30 @@ on:
- '**'

jobs:
release-macros:
opentelemetry:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/macros')
if: startsWith(github.ref, 'refs/tags/opentelemetry/v')
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
token: ${{ secrets.PAT }}
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- run: |
sudo apt update -y && sudo apt install curl -y
sudo curl -fsSL https://github.com/mikefarah/yq/releases/download/v4.35.1/yq_linux_amd64 -o /usr/bin/yq
sudo chmod +x /usr/bin/yq
name: Install yq
# Below we give some time to make sure that the macros crate is published before the lib crate
# in the case that the new lib crate depends on the new, yet to be published macros crate.
- run: timeout 15m bash -c 'until ./scripts/ci/assert-macros-crate-published.sh; do sleep 10; done'
- run: cargo publish -p pyo3-opentelemetry --token ${{ secrets.CRATES_IO_TOKEN }}
opentelemetry-macros:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/opentelemetry-macros/v')
steps:
- uses: actions/checkout@v3
with:
Expand All @@ -19,9 +40,9 @@ jobs:
toolchain: stable
override: true
- run: cargo publish -p pyo3-opentelemetry-macros --token ${{ secrets.CRATES_IO_TOKEN }}
release-lib:
tracing-subscriber:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/lib')
if: startsWith(github.ref, 'refs/tags/tracing-subscriber')
steps:
- uses: actions/checkout@v3
with:
Expand All @@ -31,12 +52,5 @@ jobs:
with:
toolchain: stable
override: true
- run: |
sudo apt update -y && sudo apt install curl -y
sudo curl -fsSL https://github.com/mikefarah/yq/releases/download/v4.35.1/yq_linux_amd64 -o /usr/bin/yq
sudo chmod +x /usr/bin/yq
name: Install yq
# Below we give some time to make sure that the macros crate is published before the lib crate
# in the case that the new lib crate depends on the new, yet to be published macros crate.
- run: timeout 15m bash -c 'until ./scripts/ci/assert-macros-crate-published.sh; do sleep 10; done'
- run: cargo publish -p pyo3-opentelemetry --token ${{ secrets.CRATES_IO_TOKEN }}
- run: cargo publish -p pyo3-tracing-subscriber --token ${{ secrets.CRATES_IO_TOKEN }}

9 changes: 4 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
name: Release

on:
workflow_dispatch: {}
on: workflow_dispatch

jobs:
release:
Expand All @@ -15,10 +14,10 @@ jobs:
with:
fetch-depth: 0
token: ${{ secrets.PAT }}
- uses: actions-rs/toolchain@v1
- name: Install Knope
uses: knope-dev/action@v2.0.0
with:
toolchain: stable
- run: cargo install knope --version 0.13.2
version: 0.13.2
- run: |
git config --global user.name "${{ github.triggering_actor }}"
git config --global user.email "${{ github.triggering_actor}}@users.noreply.github.com"
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ scratch/

# ignore to support developer-specific configuration
bacon.toml

.DS_Store
12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,15 @@ members = ["crates/*", "examples/*"]
resolver = "2"

[workspace.dependencies]
thiserror = "1.0.49"
serde = { version = "1.0.188", features = ["derive"] }
opentelemetry = { version = "0.20.0" }
opentelemetry_api = { version = "0.20.0" }
opentelemetry_sdk = { version = "0.20.0" }
pyo3 = { version = "0.19.0", features = ["macros"], default-features = false }
rstest = "0.17.0"
tokio = { version = "1.27.0", features = [] }
tracing = "0.1.37"
tracing-opentelemetry = "0.21.0"
tracing-subscriber = "0.3.17"

15 changes: 3 additions & 12 deletions Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# We exclude --no-default-features because pyo3 will not be able to link to python.
CARGO_HACK_COMMON_FLAGS = "--feature-powerset --optional-deps --exclude-no-default-features"
CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
CARGO_MAKE_WORKSPACE_SKIP_MEMBERS = "examples/*"

[tasks.clean]
clear = true
Expand Down Expand Up @@ -193,25 +194,15 @@
command = "cargo"
args = ["outdated"]

[tasks.test]
clear = true
dependencies = ["install-cargo-hack"]
env = { CARGO_MAKE_WORKSPACE_SKIP_MEMBERS = "examples/*"}
script = "cargo hack test $CARGO_HACK_COMMON_FLAGS"

[tasks.test-nextest]
[tasks.nextest]
dependencies = ["install-cargo-hack", "install-cargo-nextest"]
script = "cargo hack nextest run $CARGO_HACK_COMMON_FLAGS"

[tasks.example-lib-python]
cwd = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/examples/pyo3-opentelemetry-lib"
script = "cargo make python-check-all"

[tasks.examples]
dependencies = ["example-lib-python"]

[tasks.check-all]
dependencies = ["check", "clippy", "deny", "deadlinks", "msrv-verify", "examples", "test"]
dependencies = ["check", "clippy", "deny", "deadlinks", "msrv-verify", "nextest"]

[tasks.pre-ci-flow]
dependencies = ["check", "clippy", "deny", "deadlinks", "msrv-verify"]
Expand Down
69 changes: 7 additions & 62 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,66 +1,20 @@
# PyO3 OpenTelemetry
# PyO3 Tracing Crates

## Background
This repository contains four crates to support the ability of upstream `pyo3` extension modules to support the ability of their dependents to gather tracing telemetry from the instrumented Rust source code:

### What this is
* [crates/opentelemetry](./crates/opentelemetry): propagates OpenTelemetry context from Python into Rust.
* [crates/opentelemetry-macros](./crates/opentelemetry-macros): defines proc macros for `pyo3-opentelemetry`.
* [crates/tracing-subscriber](./crates/tracing-subscriber): supports configuration and initialization of Rust tracing subscribers from Python.

pyo3_opentelemetry provides a macro to simply and easily instrument your PyO3 bindings so that OpenTelemetry contexts can be easily passed from a Python caller into a Rust library. The `#[pypropagate]` macro instruments your Rust functions for you so that the global Python OpenTelemetry context is shared across the FFI boundary.

### What this is not

* This (currently) does not support propagating an OpenTelemetry context from Rust into Python.
* This does not "magically" instrument Rust code. Without the `#[pypropagate]` attribute, Rust code is unaffected and will not attach the Python OpenTelemetry context.
* This does not facilitate the processing or collection of OpenTelemetry spans; you still need to initialize and flush tracing providers and subscribers separately in Python and Rust. For more information, please see the respective OpenTelemetry documentation for [Python](https://opentelemetry.io/docs/instrumentation/python/) and [Rust](https://opentelemetry.io/docs/instrumentation/rust/).


### What this is

This repo contains utilities for automatically passing OpenTelemetry contexts from Python into Rust.

### What this is not

* This does not facilitate the processing of spans. You still need to separately instrument OpenTelemetry processors on both the Rust and Python side.
* This does not allow you to propagate context into _any_ Rust code from Python. It requires instrumentation of the underlying Rust source code.
* While this repository could extend to pass OpenTelemetry contexts _from Rust into Python_, it currently does not.

## Usage

From Rust:

```rs
use pyo3_opentelemetry::prelude::*;
use pyo3::prelude::*;
use tracing::instrument;

#[pypropagate]
#[pyfunction]
#[instrument]
fn my_function() {
println!("span \"my_function\" is active and will share the Python OpenTelemetry context");
}

#[pymodule]
fn my_module(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(my_function, m)?)?;
Ok(())
}
```

These features require no Python code changes, however, [opentelemetry-api](https://pypi.org/project/opentelemetry-api/) must be installed.
For a functional example of usage of all of these crates, see [examples/pyo3-opentelemetry-lib](./examples/pyo3-opentelemetry-lib).

## Development

| Directory | Purpose |
|-----------|---------|
| crates/macros | Rust macro definitions |
| crates/lib | Supporting Rust functions that get OTel context from Python. |
| examples/pyo3-opentelemetry-lib | maturin PyO3 project with Python test assertions on Rust OTel spans |

### Rust

It should be sufficient to [install the Rust toolchain](https://rustup.rs/) and [cargo-make](https://github.com/sagiegurari/cargo-make). Then:

```sh
```shell
cargo make check-all
```

Expand All @@ -71,12 +25,3 @@ Install:
* Python - installation through [pyenv](https://github.com/pyenv/pyenv) is recommended.
* [Poetry](https://python-poetry.org/docs/#installation) - for installing plain Python dependencies.

#### Python Example

[examples/pyo3-opentelemetry-lib](./examples/pyo3-opentelemetry-lib/) contains a full end-to-end pyo3 project with pytests. To build and run them:

```sh
cd examples/pyo3-opentelemetry-lib
cargo make python-check-all
```

File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"
categories = ["Python bindings", "OpenTelemetry", "Tracing", "Macros"]
keywords = ["python", "pyo3", "opentelemetry", "tracing"]
license = "MIT OR Apache-2.0"
readme = "../../README.md"
readme = "../opentelemetry/README.md"
description = "Macro for passing OpenTelemetry context from Python to Rust"
repository = "https://github.com/rigetti/pyo3-opentelemetry"
rust-version = "1.65.0"
Expand All @@ -21,4 +21,4 @@ quote = "1.0.26"
syn = { version = "2.0.14", features = ["full", "derive"] }

[dev-dependencies]
rstest = "0.17.0"
rstest = { workspace = true }
File renamed without changes.
File renamed without changes.
27 changes: 13 additions & 14 deletions crates/lib/Cargo.toml → crates/opentelemetry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,31 @@ edition = "2021"
categories = ["Python bindings", "OpenTelemetry", "Tracing", "Macros"]
keywords = ["python", "pyo3", "opentelemetry", "tracing"]
license = "Apache-2.0"
readme = "../../README.md"
readme = "./README.md"
description = "Macro and utilities for passing OpenTelemetry context from Python to Rust"
repository = "https://github.com/rigetti/pyo3-opentelemetry"
rust-version = "1.65.0"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "pyo3_opentelemetry"
crate-type = ["lib", "cdylib"]
crate-type = ["lib"]

[dependencies]
opentelemetry = "0.18.0"
opentelemetry_api = "0.18.0"
opentelemetry_sdk = "0.18.0"
pyo3 = { version = "0.18.0", features = [] }
pyo3-opentelemetry-macros = { path = "../macros", version = "0.2.0" }
opentelemetry = { workspace = true }
opentelemetry_api = { workspace = true }
opentelemetry_sdk = { workspace = true }
pyo3 = { workspace = true }
pyo3-opentelemetry-macros = { path = "../opentelemetry-macros", version = "0.2.0" }

[dev-dependencies]
futures-util = "0.3.28"
once_cell = "1.17.1"
opentelemetry = { version = "0.18.0", features = ["trace", "rt-tokio"] }
tokio = { version = "1.27.0", features = ["sync", "parking_lot", "macros"] }
tracing = "0.1.37"
tracing-opentelemetry = "0.18.0"
tracing-subscriber = "0.3.16"
opentelemetry = { workspace = true, features = ["trace", "rt-tokio"] }
tokio = { workspace = true, features = ["sync", "parking_lot", "macros"] }
tracing = { workspace = true }
tracing-opentelemetry = { workspace = true }
tracing-subscriber = { workspace = true }

[features]
extension-module = ["pyo3/extension-module"]
default = ["extension-module"]

42 changes: 42 additions & 0 deletions crates/opentelemetry/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# PyO3 OpenTelemetry

## Background

### What this is

pyo3_opentelemetry provides a macro to simply and easily instrument your PyO3 bindings so that OpenTelemetry contexts can be easily passed from a Python caller into a Rust library. The `#[pypropagate]` macro instruments your Rust functions for you so that the global Python OpenTelemetry context is shared across the FFI boundary.

### What this is not

* This (currently) does not support propagating an OpenTelemetry context from Rust into Python.
* This does not "magically" instrument Rust code. Without the `#[pypropagate]` attribute, Rust code is unaffected and will not attach the Python OpenTelemetry context.
* This does not facilitate the processing or collection of OpenTelemetry spans; you still need to initialize and flush tracing providers and subscribers separately in Python and Rust. For more information, please see the respective OpenTelemetry documentation for [Python](https://opentelemetry.io/docs/instrumentation/python/) and [Rust](https://opentelemetry.io/docs/instrumentation/rust/).

## Usage

> For a complete functioning example, see the `examples/pyo3-opentelemetry-lib/src/lib.rs` example within this crate's repository.

From Rust:

```rust
use pyo3_opentelemetry::prelude::*;
use pyo3::prelude::*;
use tracing::instrument;

#[pypropagate]
#[pyfunction]
#[instrument]
fn my_function() {
println!("span \"my_function\" is active and will share the Python OpenTelemetry context");
}

#[pymodule]
fn my_module(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(my_function, m)?)?;
Ok(())
}
```

These features require no Python code changes, however, [opentelemetry-api](https://pypi.org/project/opentelemetry-api/) must be installed.


Loading
Loading