Skip to content

Commit c48428f

Browse files
committed
add support for inferring token from region
1 parent 367c983 commit c48428f

File tree

4 files changed

+67
-13
lines changed

4 files changed

+67
-13
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ thiserror = "2"
3333
serde = { version = "1", features = ["derive"], optional = true }
3434
nu-ansi-term = "0.50.1"
3535
chrono = "0.4.39"
36+
regex = "1.11.1"
3637

3738
[dev-dependencies]
3839
async-trait = "0.1.88"

examples/basic.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ static BASIC_COUNTER: LazyLock<Counter<u64>> = LazyLock::new(|| {
1212
});
1313

1414
fn main() -> Result<(), Box<dyn std::error::Error>> {
15-
let shutdown_handler = logfire::configure().install_panic_handler().finish()?;
15+
let shutdown_handler = logfire::configure()
16+
.install_panic_handler()
17+
.with_console(None)
18+
.finish()?;
1619

1720
logfire::info!("Hello, world!");
1821

src/config.rs

+38-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use opentelemetry_sdk::{
1212
metrics::reader::MetricReader,
1313
trace::{IdGenerator, SpanProcessor},
1414
};
15+
use regex::Regex;
1516

1617
use crate::ConfigureError;
1718

@@ -170,7 +171,7 @@ impl std::fmt::Debug for Target {
170171
/// Options primarily used for testing by Logfire developers.
171172
pub struct AdvancedOptions {
172173
/// Root URL for the Logfire API.
173-
pub(crate) base_url: String,
174+
pub(crate) base_url: Option<String>,
174175
/// Generator for trace and span IDs.
175176
pub(crate) id_generator: Option<BoxedIdGenerator>,
176177
/// Resource to override default resource detection.
@@ -189,7 +190,7 @@ pub struct AdvancedOptions {
189190
impl Default for AdvancedOptions {
190191
fn default() -> Self {
191192
AdvancedOptions {
192-
base_url: "https://logfire-api.pydantic.dev".to_string(),
193+
base_url: None,
193194
id_generator: None,
194195
resource: None,
195196
}
@@ -200,7 +201,7 @@ impl AdvancedOptions {
200201
/// Set the base URL for the Logfire API.
201202
#[must_use]
202203
pub fn with_base_url<T: AsRef<str>>(mut self, base_url: T) -> Self {
203-
self.base_url = base_url.as_ref().into();
204+
self.base_url = Some(base_url.as_ref().into());
204205
self
205206
}
206207

@@ -222,6 +223,40 @@ impl AdvancedOptions {
222223
}
223224
}
224225

226+
struct RegionData {
227+
base_url: &'static str,
228+
#[expect(dead_code)] // not used for the moment
229+
gcp_region: &'static str,
230+
}
231+
232+
const US_REGION: RegionData = RegionData {
233+
base_url: "https://logfire-us.pydantic.dev",
234+
gcp_region: "us-east4",
235+
};
236+
237+
const EU_REGION: RegionData = RegionData {
238+
base_url: "https://logfire-eu.pydantic.dev",
239+
gcp_region: "europe-west4",
240+
};
241+
242+
/// Get the base API URL from the token's region.
243+
pub(crate) fn get_base_url_from_token(token: &str) -> &'static str {
244+
let pydantic_logfire_token_pattern = Regex::new(
245+
r#"^(?P<safe_part>pylf_v(?P<version>[0-9]+)_(?P<region>[a-z]+)_)(?P<token>[a-zA-Z0-9]+)$"#,
246+
)
247+
.expect("token regex is known to be valid");
248+
249+
match pydantic_logfire_token_pattern
250+
.captures(token)
251+
.and_then(|captures| captures.name("region"))
252+
.map(|region| region.as_str())
253+
{
254+
Some("eu") => EU_REGION.base_url,
255+
// fallback to US region if the token / region is not recognized
256+
Some("us") | _ => US_REGION.base_url,
257+
}
258+
}
259+
225260
/// Configuration of metrics.
226261
///
227262
/// This only has one option for now, but it's a place to add more related options in the future.

src/lib.rs

+24-9
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ use std::sync::{Arc, Once};
103103
use std::{backtrace::Backtrace, env::VarError, sync::OnceLock, time::Duration};
104104

105105
use bridges::tracing::LogfireTracingPendingSpanNotSentLayer;
106+
use config::get_base_url_from_token;
106107
use opentelemetry::trace::TracerProvider;
107108
use opentelemetry_sdk::metrics::{PeriodicReader, SdkMeterProvider};
108109
use opentelemetry_sdk::trace::{
@@ -501,17 +502,29 @@ impl LogfireConfigBuilder {
501502

502503
let mut http_headers: Option<HashMap<String, String>> = None;
503504

504-
if send_to_logfire {
505+
let logfire_base_url = if send_to_logfire {
505506
let Some(token) = token else {
506507
return Err(ConfigureError::TokenRequired);
507508
};
509+
508510
http_headers
509511
.get_or_insert_default()
510512
.insert("Authorization".to_string(), format!("Bearer {token}"));
511513

514+
Some(
515+
advanced_options
516+
.base_url
517+
.as_deref()
518+
.unwrap_or_else(|| get_base_url_from_token(&token)),
519+
)
520+
} else {
521+
None
522+
};
523+
524+
if let Some(logfire_base_url) = logfire_base_url {
512525
tracer_provider_builder = tracer_provider_builder.with_span_processor(
513526
BatchSpanProcessor::builder(exporters::span_exporter(
514-
&advanced_options.base_url,
527+
logfire_base_url,
515528
http_headers.clone(),
516529
)?)
517530
.with_batch_config(
@@ -570,14 +583,16 @@ impl LogfireConfigBuilder {
570583

571584
let mut meter_provider_builder = SdkMeterProvider::builder();
572585

573-
if send_to_logfire && self.enable_metrics {
574-
let metric_reader = PeriodicReader::builder(exporters::metric_exporter(
575-
&advanced_options.base_url,
576-
http_headers,
577-
)?)
578-
.build();
586+
if let Some(logfire_base_url) = logfire_base_url {
587+
if self.enable_metrics {
588+
let metric_reader = PeriodicReader::builder(exporters::metric_exporter(
589+
logfire_base_url,
590+
http_headers,
591+
)?)
592+
.build();
579593

580-
meter_provider_builder = meter_provider_builder.with_reader(metric_reader);
594+
meter_provider_builder = meter_provider_builder.with_reader(metric_reader);
595+
}
581596
};
582597

583598
if let Some(metrics) = self.metrics.filter(|_| self.enable_metrics) {

0 commit comments

Comments
 (0)