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

Add optional opentelemetry file export tracing #723

Merged
merged 37 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
c70bf80
Add trace feature
joshwlewis Nov 6, 2023
523603c
Add tracing for detect
joshwlewis Nov 8, 2023
d68c36b
Add tracing for buildpack build
joshwlewis Nov 8, 2023
aae2e57
Add a tracing test
joshwlewis Nov 8, 2023
af37182
Add service.version to resource span
joshwlewis Nov 8, 2023
efb2325
Cleanup clippy issue on newer rust
joshwlewis Nov 8, 2023
ac0fa8d
Add changelog entry for trace feature
joshwlewis Nov 8, 2023
5558396
Update to opentelemetry 0.21.0; use opentelemetry_sdk for sdk imports
joshwlewis Nov 9, 2023
33b2a99
Use the libcnb error rather than the io error when reporting to tracing
joshwlewis Nov 10, 2023
adb88f9
Add some documentation to tracing.rs
joshwlewis Nov 15, 2023
2a9cae5
Move telemetry path to libcnb
joshwlewis Nov 15, 2023
3f5cb51
Add tracing integration test
joshwlewis Nov 28, 2023
4edd307
Verify that tracing data is valid JSON
joshwlewis Nov 28, 2023
5b911db
Additionally transform hyphens to underscores for trace names
joshwlewis Nov 28, 2023
c43b217
Fix spelling typo
joshwlewis Nov 30, 2023
982ca52
Slight update to test buildpack documentation
joshwlewis Nov 30, 2023
d829c56
Use workspace lints in test buildpack
joshwlewis Dec 4, 2023
8cdb904
Prefer workspace lints
joshwlewis Dec 4, 2023
02cadc8
Prefer workspace lints
joshwlewis Dec 4, 2023
71c3ba3
Use workspace dependency
joshwlewis Dec 4, 2023
8a7c4c9
Be specific about which crate
joshwlewis Dec 4, 2023
56ee596
Use an array over a vector
joshwlewis Dec 4, 2023
3664926
Drop redundant clone
joshwlewis Dec 4, 2023
c242415
Silence unused crate warning for serde_json
joshwlewis Dec 4, 2023
0c18d96
Update tracing documentation and comments
joshwlewis Dec 4, 2023
690e0d7
Use set_error in when failing to write sboms
joshwlewis Dec 4, 2023
1c6b7c7
Fail if CARGO_PKG_NAME is unset for tracing
joshwlewis Dec 4, 2023
f6486ea
Set error on traces in more error scenarios
joshwlewis Dec 5, 2023
3f641b0
Drop unnecessary vec allocations
joshwlewis Dec 5, 2023
ff02453
Fix a few clippy lints
joshwlewis Dec 5, 2023
f4b440e
Silence a few more clippy lints
joshwlewis Dec 5, 2023
6bf7261
Clean up runtime repeated use of cfg feature check
joshwlewis Dec 5, 2023
244fe14
Better name for closure
joshwlewis Dec 5, 2023
e11cb55
Add -u to bash test buildpacks
joshwlewis Dec 5, 2023
74a5420
More rigorous assertions for tracing contents
joshwlewis Dec 5, 2023
afe1d86
Move ? to a more traditional spot
joshwlewis Dec 5, 2023
e109d74
Add OpenTelemetry to clippy valid idents rather than allowing doc_mar…
joshwlewis Dec 5, 2023
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- `libcnb`:
- An optional `trace` feature has been added that emits OpenTelemetry tracing
data to a [File Export](https://opentelemetry.io/docs/specs/otel/protocol/file-exporter/). ([#723](https://github.com/heroku/libcnb.rs/pull/723))

## [0.16.0] - 2023-11-17

Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ members = [
"test-buildpacks/readonly-layer-files",
"test-buildpacks/sbom",
"test-buildpacks/store",
"test-buildpacks/tracing",
]

[workspace.package]
Expand Down
1 change: 1 addition & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
allow-unwrap-in-tests = true
doc-valid-idents = ["OpenTelemetry", ".."]
7 changes: 7 additions & 0 deletions libcnb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,23 @@ include = ["src/**/*", "LICENSE", "README.md"]
[lints]
workspace = true

[features]
trace = ["dep:opentelemetry", "dep:opentelemetry_sdk", "dep:opentelemetry-stdout"]

[dependencies]
anyhow = { version = "1.0.75", optional = true }
cyclonedx-bom = { version = "0.4.3", optional = true }
libcnb-common.workspace = true
libcnb-data.workspace = true
libcnb-proc-macros.workspace = true
opentelemetry = { version = "0.21.0", optional = true }
opentelemetry_sdk = { version = "0.21.0", optional = true }
opentelemetry-stdout = { version = "0.2.0", optional = true, features = ["trace"] }
serde = { version = "1.0.192", features = ["derive"] }
thiserror = "1.0.50"
toml.workspace = true

[dev-dependencies]
fastrand = "2.0.1"
tempfile = "3.8.1"
serde_json = "1.0.108"
5 changes: 5 additions & 0 deletions libcnb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ mod error;
mod exit_code;
mod platform;
mod runtime;
#[cfg(feature = "trace")]
mod tracing;
mod util;

joshwlewis marked this conversation as resolved.
Show resolved Hide resolved
pub use buildpack::Buildpack;
Expand All @@ -27,6 +29,9 @@ pub use libcnb_common::toml_file::*;
pub use platform::*;
pub use runtime::*;

#[cfg(all(test, not(feature = "trace")))]
use serde_json as _;

/// Provides types for CNB data formats. Is a re-export of the `libcnb-data` crate.
#[doc(inline)]
pub use libcnb_data as data;
Expand Down
137 changes: 110 additions & 27 deletions libcnb/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ use crate::detect::{DetectContext, InnerDetectResult};
use crate::error::Error;
use crate::platform::Platform;
use crate::sbom::cnb_sbom_path;
#[cfg(feature = "trace")]
use crate::tracing::start_trace;
use crate::util::is_not_found_error_kind;
use crate::{exit_code, TomlFileError, LIBCNB_SUPPORTED_BUILDPACK_API};
use libcnb_common::toml_file::{read_toml_file, write_toml_file};
use libcnb_data::buildpack::ComponentBuildpackDescriptor;
use libcnb_data::store::Store;
use serde::de::DeserializeOwned;
use serde::Deserialize;
Expand Down Expand Up @@ -121,31 +124,63 @@ pub fn libcnb_runtime_detect<B: Buildpack>(
) -> crate::Result<i32, B::Error> {
let app_dir = env::current_dir().map_err(Error::CannotDetermineAppDirectory)?;

let buildpack_dir = read_buildpack_dir()?;

let buildpack_descriptor: ComponentBuildpackDescriptor<<B as Buildpack>::Metadata> =
read_buildpack_descriptor()?;

#[cfg(feature = "trace")]
let mut trace = start_trace(&buildpack_descriptor.buildpack, "detect");

let mut trace_error = |err: &dyn std::error::Error| {
#[cfg(feature = "trace")]
trace.set_error(err);
};
let stack_id: StackId = env::var("CNB_STACK_ID")
.map_err(Error::CannotDetermineStackId)
.and_then(|stack_id_string| stack_id_string.parse().map_err(Error::StackIdError))?;

let platform = B::Platform::from_path(&args.platform_dir_path)
.map_err(Error::CannotCreatePlatformFromPath)?;
.and_then(|stack_id_string| stack_id_string.parse().map_err(Error::StackIdError))
.map_err(|err| {
trace_error(&err);
err
})?;

let platform = B::Platform::from_path(&args.platform_dir_path).map_err(|inner_err| {
let err = Error::CannotCreatePlatformFromPath(inner_err);
trace_error(&err);
err
})?;

let build_plan_path = args.build_plan_path;

let detect_context = DetectContext {
app_dir,
buildpack_dir,
stack_id,
platform,
buildpack_dir: read_buildpack_dir()?,
buildpack_descriptor: read_buildpack_descriptor()?,
buildpack_descriptor,
};

match buildpack.detect(detect_context)?.0 {
InnerDetectResult::Fail => Ok(exit_code::DETECT_DETECTION_FAILED),
let detect_result = buildpack.detect(detect_context).map_err(|err| {
trace_error(&err);
err
})?;

match detect_result.0 {
InnerDetectResult::Fail => {
#[cfg(feature = "trace")]
trace.add_event("detect-failed");
Ok(exit_code::DETECT_DETECTION_FAILED)
}
InnerDetectResult::Pass { build_plan } => {
if let Some(build_plan) = build_plan {
write_toml_file(&build_plan, build_plan_path)
.map_err(Error::CannotWriteBuildPlan)?;
write_toml_file(&build_plan, build_plan_path).map_err(|inner_err| {
let err = Error::CannotWriteBuildPlan(inner_err);
trace_error(&err);
err
})?;
}

#[cfg(feature = "trace")]
trace.add_event("detect-passed");
Ok(exit_code::DETECT_DETECTION_PASSED)
}
}
Expand All @@ -163,31 +198,63 @@ pub fn libcnb_runtime_build<B: Buildpack>(

let app_dir = env::current_dir().map_err(Error::CannotDetermineAppDirectory)?;

let buildpack_dir = read_buildpack_dir()?;

let buildpack_descriptor: ComponentBuildpackDescriptor<<B as Buildpack>::Metadata> =
read_buildpack_descriptor()?;

#[cfg(feature = "trace")]
let mut trace = start_trace(&buildpack_descriptor.buildpack, "build");

let mut trace_error = |err: &dyn std::error::Error| {
#[cfg(feature = "trace")]
trace.set_error(err);
};

let stack_id: StackId = env::var("CNB_STACK_ID")
.map_err(Error::CannotDetermineStackId)
.and_then(|stack_id_string| stack_id_string.parse().map_err(Error::StackIdError))?;

let platform = Platform::from_path(&args.platform_dir_path)
.map_err(Error::CannotCreatePlatformFromPath)?;
.and_then(|stack_id_string| stack_id_string.parse().map_err(Error::StackIdError))
.map_err(|err| {
trace_error(&err);
err
})?;

let platform = Platform::from_path(&args.platform_dir_path).map_err(|inner_err| {
let err = Error::CannotCreatePlatformFromPath(inner_err);
trace_error(&err);
err
})?;

let buildpack_plan =
read_toml_file(&args.buildpack_plan_path).map_err(Error::CannotReadBuildpackPlan)?;
let buildpack_plan = read_toml_file(&args.buildpack_plan_path).map_err(|inner_err| {
let err = Error::CannotReadBuildpackPlan(inner_err);
trace_error(&err);
err
})?;

let store = match read_toml_file::<Store>(layers_dir.join("store.toml")) {
Err(TomlFileError::IoError(io_error)) if is_not_found_error_kind(&io_error) => Ok(None),
other => other.map(Some),
}
.map_err(Error::CannotReadStore)?;
.map_err(Error::CannotReadStore)
.map_err(|err| {
trace_error(&err);
err
})?;

let build_result = buildpack.build(BuildContext {
let build_context = BuildContext {
layers_dir: layers_dir.clone(),
app_dir,
stack_id,
platform,
buildpack_plan,
buildpack_dir: read_buildpack_dir()?,
buildpack_descriptor: read_buildpack_descriptor()?,
buildpack_dir,
buildpack_descriptor,
store,
};

let build_result = buildpack.build(build_context).map_err(|err| {
trace_error(&err);
err
})?;

match build_result.0 {
Expand All @@ -198,31 +265,47 @@ pub fn libcnb_runtime_build<B: Buildpack>(
launch_sboms,
} => {
if let Some(launch) = launch {
write_toml_file(&launch, layers_dir.join("launch.toml"))
.map_err(Error::CannotWriteLaunch)?;
write_toml_file(&launch, layers_dir.join("launch.toml")).map_err(|inner_err| {
let err = Error::CannotWriteLaunch(inner_err);
trace_error(&err);
err
})?;
};

if let Some(store) = store {
write_toml_file(&store, layers_dir.join("store.toml"))
.map_err(Error::CannotWriteStore)?;
write_toml_file(&store, layers_dir.join("store.toml")).map_err(|inner_err| {
let err = Error::CannotWriteStore(inner_err);
trace_error(&err);
err
})?;
};

for build_sbom in build_sboms {
fs::write(
cnb_sbom_path(&build_sbom.format, &layers_dir, "build"),
&build_sbom.data,
)
.map_err(Error::CannotWriteBuildSbom)?;
.map_err(Error::CannotWriteBuildSbom)
.map_err(|err| {
trace_error(&err);
err
})?;
}

for launch_sbom in launch_sboms {
fs::write(
cnb_sbom_path(&launch_sbom.format, &layers_dir, "launch"),
&launch_sbom.data,
)
.map_err(Error::CannotWriteLaunchSbom)?;
.map_err(Error::CannotWriteLaunchSbom)
.map_err(|err| {
trace_error(&err);
err
})?;
}

#[cfg(feature = "trace")]
trace.add_event("build-success");
Ok(exit_code::GENERIC_SUCCESS)
}
}
Expand Down
Loading