Skip to content

Add KVP Tracer and OpenTel #83

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

Closed
wants to merge 8 commits into from
Closed
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
9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,19 @@ description = "A reference implementation for provisioning Linux VMs on Azure."
exitcode = "1.1.2"
anyhow = "1.0.81"
tokio = { version = "1", features = ["full"] }
opentelemetry_sdk = "0.21"
opentelemetry = "0.21"
tracing = "0.1"


[dependencies.libazureinit]
path = "libazureinit"
version = "0.1.0"

[dependencies.azurekvp]
path = "azurekvp"
version = "0.1.0"

[profile.dev]
incremental = true

Expand All @@ -31,4 +39,5 @@ path = "tests/functional_tests.rs"
[workspace]
members = [
"libazureinit",
"azurekvp",
]
26 changes: 26 additions & 0 deletions azurekvp/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "azurekvp"
version = "0.1.1"
edition = "2021"
repository = "https://github.com/Azure/azure-init/"
homepage = "https://github.com/Azure/azure-init/"
license = "MIT"
description = "A binary library for implementing OpenTelemetry KVP for Linux VMs on Azure."


[dependencies]
once_cell = "1.12.0"
opentelemetry = "0.18.0"
opentelemetry_sdk = "0.22.0"
opentelemetry-stdout = "0.2.0"
tracing = "0.1"
tracing-opentelemetry = "0.18.0"
tracing-subscriber = "0.3.11"
serde = { version = "1.0" }
serde_json = "1.0.68"
tokio = { version = "1", features = ["full"] }
libc = "0.2"

[lib]
name = "azurekvp"
path = "src/tracing.rs"
4 changes: 4 additions & 0 deletions azurekvp/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

pub mod main;
50 changes: 50 additions & 0 deletions azurekvp/src/tracing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use opentelemetry::{ trace::{self, TracerProvider}, trace::Tracer as _};
use opentelemetry::sdk::trace as sdktrace;
use opentelemetry::sdk::export::trace::stdout;
use opentelemetry::global;
use tracing_opentelemetry::OpenTelemetryLayer;
use once_cell::sync::Lazy;
use std::fs::File;
use std::os::unix::io::AsRawFd;
use libc::dup2;
use tracing_subscriber::{Registry, layer::SubscriberExt};


pub static TRACER: Lazy<()> = Lazy::new(|| {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend that we move the code within this block into the binary in the azure-init crate. Each application tends to want to configure its logging in its own way - different file names, different environment variables for configuration, different filtering rules, etc.

What would be good in a library is any custom Layers we end up making, if any. These can be consumed (and configured) by applications in their tracing-subscriber setup.

// Redirect stdout to a file
let log_file_path = "spans.log";
let file = File::create(log_file_path).expect("Failed to create log file");
let stdout_fd = file.as_raw_fd();
unsafe {
dup2(stdout_fd, libc::STDOUT_FILENO);
}

// Set up the stdout exporter correctly
let exporter = stdout::Exporter::new(std::io::stdout(), true);

// Set up the TracerProvider with the stdout exporter
let provider = sdktrace::TracerProvider::builder()
.with_simple_exporter(exporter)
.build();

global::set_tracer_provider(provider.clone());

let tracer = provider.tracer("azure-kvp");
// Create the OpenTelemetry layer using the SDK tracer
let otel_layer = OpenTelemetryLayer::new(tracer);

// Create a `tracing` subscriber
let subscriber = Registry::default()
.with(otel_layer);
Comment on lines +38 to +41
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tracing subscriber lets you register multiple layers, and each layer can be configured with its own filters and formatters and so on. It takes a little digging in the tracing_subscriber docs and I forever forget how to do it properly, but here's an example that would let us avoid needing to duplicate the file descriptor:

Suggested change
// Create a `tracing` subscriber
let subscriber = Registry::default()
.with(otel_layer);
// Create a tracing layer for logging to stdout
let log_file = Arc::new(File::create("example.log").unwrap());
let stderr_layer = tracing_subscriber::fmt::layer()
.with_writer(std::io::stderr.and(log_file))
.with_filter(tracing_subscriber::EnvFilter::from_env("AZURE_INIT_LOG"));
// Create a `tracing` subscriber
let subscriber = Registry::default()
.with(stderr_layer)
.with(otel_layer);

This adds a second layer which writes to stderr as well as a file, and applies a filter you can define (requires env-filter feature on the tracing-subscriber crate) in the AZURE_INIT_LOG environment variable. We don't necessarily need to use EnvFilter, but I tend to start with it because it's easy to get started with. I tend to abandon it if I start using a configuration file for the application.

If you run with this diff, you'll get logs written to stderr and "example.log" for that layer, and then separately to "spans.log". I don't think we want this literal suggestion, but I think this is the general approach we want to take when setting things up.

We can also apply a global filter that applies to all layers instead (or in addition to) filters on each layer.


// Set the subscriber as the global default
tracing::subscriber::set_global_default(subscriber)
.expect("Setting default subscriber failed");
});

pub fn initialize_tracing() {
Lazy::force(&TRACER);
}
Loading
Loading