Skip to content

Commit 701d430

Browse files
committed
add sentry frontend error reporting & performance monitoring
1 parent 8522660 commit 701d430

File tree

5 files changed

+83
-27
lines changed

5 files changed

+83
-27
lines changed

src/bin/cratesfyi.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,16 @@ fn main() {
4141
.from_env_lossy(),
4242
);
4343

44-
let _sentry_guard = if let Ok(sentry_dsn) = env::var("SENTRY_DSN") {
44+
let ctx = BinContext::new();
45+
let config = match ctx.config().context("could not load config") {
46+
Ok(config) => config,
47+
Err(err) => {
48+
eprintln!("{:?}", err);
49+
std::process::exit(1);
50+
}
51+
};
52+
53+
let _sentry_guard = if let Some(ref sentry_dsn) = config.sentry_dsn {
4554
tracing::subscriber::set_global_default(tracing_registry.with(
4655
sentry_tracing::layer().event_filter(|md| {
4756
if md.fields().field("reported_to_sentry").is_some() {
@@ -58,10 +67,7 @@ fn main() {
5867
sentry::ClientOptions {
5968
release: Some(docs_rs::BUILD_VERSION.into()),
6069
attach_stacktrace: true,
61-
traces_sample_rate: env::var("SENTRY_TRACES_SAMPLE_RATE")
62-
.ok()
63-
.and_then(|v| v.parse().ok())
64-
.unwrap_or(0.0),
70+
traces_sample_rate: config.sentry_traces_sample_rate.unwrap_or(0.0),
6571
..Default::default()
6672
}
6773
.add_integration(sentry_panic::PanicIntegration::default()),
@@ -71,7 +77,7 @@ fn main() {
7177
None
7278
};
7379

74-
if let Err(err) = CommandLine::parse().handle_args() {
80+
if let Err(err) = CommandLine::parse().handle_args(ctx) {
7581
let mut msg = format!("Error: {err}");
7682
for cause in err.chain() {
7783
write!(msg, "\n\nCaused by:\n {cause}").unwrap();
@@ -156,9 +162,7 @@ enum CommandLine {
156162
}
157163

158164
impl CommandLine {
159-
fn handle_args(self) -> Result<()> {
160-
let ctx = BinContext::new();
161-
165+
fn handle_args(self, ctx: BinContext) -> Result<()> {
162166
match self {
163167
Self::Build { subcommand } => subcommand.handle_args(ctx)?,
164168
Self::StartRegistryWatcher {

src/config.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::{cdn::CdnKind, storage::StorageKind};
22
use anyhow::{anyhow, bail, Context, Result};
3-
use std::{env::VarError, error::Error, path::PathBuf, str::FromStr, time::Duration};
3+
use serde::ser::{Serialize, SerializeStruct, Serializer};
4+
use std::{env::VarError, error::Error, path::PathBuf, str::FromStr, sync::Arc, time::Duration};
45
use tracing::trace;
56
use url::Url;
67

@@ -11,6 +12,10 @@ pub struct Config {
1112
pub registry_url: Option<String>,
1213
pub registry_api_host: Url,
1314

15+
// sentry config
16+
pub sentry_dsn: Option<sentry::types::Dsn>,
17+
pub sentry_traces_sample_rate: Option<f32>,
18+
1419
// Database connection params
1520
pub(crate) database_url: String,
1621
pub(crate) max_legacy_pool_size: u32,
@@ -148,6 +153,9 @@ impl Config {
148153
)?,
149154
prefix: prefix.clone(),
150155

156+
sentry_dsn: maybe_env("SENTRY_DSN")?,
157+
sentry_traces_sample_rate: maybe_env("SENTRY_TRACES_SAMPLE_RATE")?,
158+
151159
database_url: require_env("DOCSRS_DATABASE_URL")?,
152160
max_legacy_pool_size: env("DOCSRS_MAX_LEGACY_POOL_SIZE", 45)?,
153161
max_pool_size: env("DOCSRS_MAX_POOL_SIZE", 45)?,
@@ -224,6 +232,25 @@ impl Config {
224232
}
225233
}
226234

235+
/// A more public version of the config that will be automaticaly exposed to the
236+
/// Tera context.
237+
pub(crate) struct PublicConfig(pub Arc<Config>);
238+
239+
impl Serialize for PublicConfig {
240+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
241+
where
242+
S: Serializer,
243+
{
244+
let mut state = serializer.serialize_struct("PublicConfig", 2)?;
245+
state.serialize_field("sentry_dsn", &self.0.sentry_dsn)?;
246+
state.serialize_field(
247+
"sentry_traces_sample_rate",
248+
&self.0.sentry_traces_sample_rate,
249+
)?;
250+
state.end()
251+
}
252+
}
253+
227254
fn env<T>(var: &str, default: T) -> Result<T>
228255
where
229256
T: FromStr,

src/web/csp.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ impl Csp {
7373
result.push_str("; font-src 'self'");
7474

7575
// Allow XHR.
76-
result.push_str("; connect-src 'self'");
76+
result.push_str("; connect-src 'self' *.sentry.io");
7777

7878
// Only allow scripts with the random nonce attached to them.
7979
//
@@ -83,6 +83,7 @@ impl Csp {
8383
//
8484
// This `.unwrap` is safe since the `Write` impl on str can never fail.
8585
write!(result, "; script-src 'nonce-{}'", self.nonce).unwrap();
86+
result.push_str(" https://browser.sentry-cdn.com https://js.sentry-cdn.com");
8687
}
8788

8889
fn render_svg(&self, result: &mut String) {

src/web/page/web_page.rs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
use super::TemplateData;
2-
use crate::web::{csp::Csp, error::AxumNope};
2+
use crate::{
3+
config::PublicConfig,
4+
web::{csp::Csp, error::AxumNope},
5+
Config,
6+
};
37
use anyhow::Error;
48
use axum::{
59
body::Body,
610
extract::Request as AxumRequest,
711
middleware::Next,
812
response::{IntoResponse, Response as AxumResponse},
13+
Extension,
914
};
1015
use futures_util::future::{BoxFuture, FutureExt};
1116
use http::header::CONTENT_LENGTH;
@@ -123,6 +128,7 @@ pub(crate) struct DelayedTemplateRender {
123128
fn render_response(
124129
mut response: AxumResponse,
125130
templates: Arc<TemplateData>,
131+
config: Arc<Config>,
126132
csp_nonce: String,
127133
) -> BoxFuture<'static, AxumResponse> {
128134
async move {
@@ -133,6 +139,7 @@ fn render_response(
133139
cpu_intensive_rendering,
134140
} = render;
135141
context.insert("csp_nonce", &csp_nonce);
142+
context.insert("config", &PublicConfig(config.clone()));
136143

137144
let rendered = if cpu_intensive_rendering {
138145
templates
@@ -160,6 +167,7 @@ fn render_response(
160167
return render_response(
161168
AxumNope::InternalError(err).into_response(),
162169
templates,
170+
config,
163171
csp_nonce,
164172
)
165173
.await;
@@ -179,21 +187,16 @@ fn render_response(
179187
.boxed()
180188
}
181189

182-
pub(crate) async fn render_templates_middleware(req: AxumRequest, next: Next) -> AxumResponse {
183-
let templates: Arc<TemplateData> = req
184-
.extensions()
185-
.get::<Arc<TemplateData>>()
186-
.expect("template data request extension not found")
187-
.clone();
188-
189-
let csp_nonce = req
190-
.extensions()
191-
.get::<Arc<Csp>>()
192-
.expect("csp request extension not found")
193-
.nonce()
194-
.to_owned();
190+
pub(crate) async fn render_templates_middleware(
191+
Extension(config): Extension<Arc<Config>>,
192+
Extension(templates): Extension<Arc<TemplateData>>,
193+
Extension(csp): Extension<Arc<Csp>>,
194+
req: AxumRequest,
195+
next: Next,
196+
) -> AxumResponse {
197+
let csp_nonce = csp.nonce().to_owned();
195198

196199
let response = next.run(req).await;
197200

198-
render_response(response, templates, csp_nonce).await
201+
render_response(response, templates, config, csp_nonce).await
199202
}

templates/base.html

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,27 @@
1717

1818
<title>{%- block title -%} Docs.rs {%- endblock title -%}</title>
1919

20+
{% if config.sentry_dsn %}
21+
<script
22+
src="https://browser.sentry-cdn.com/7.105.0/bundle.tracing.min.js"
23+
integrity="sha384-XFfw9fN0ZxeLnveKcWT/zLOcibWw9ewRfNiUu/teAuMfP4G3Oy7lLh7rN3rX2T7M"
24+
crossorigin="anonymous"
25+
></script>
26+
27+
<script nonce="{{ csp_nonce }}">
28+
Sentry.init({
29+
dsn: "{{ config.sentry_dsn | safe }}",
30+
release: "{{ docsrs_version() }}",
31+
integrations: [
32+
// potentially override transaction name later
33+
// https://docs.sentry.io/platforms/javascript/performance/troubleshooting/
34+
Sentry.browserTracingIntegration()
35+
],
36+
tracesSampleRate: {{ config.sentry_traces_sample_rate }},
37+
});
38+
</script>
39+
{% endif %}
40+
2041
<script nonce="{{ csp_nonce }}">{%- include "theme.js" -%}</script>
2142
{%- block css -%}{%- endblock css -%}
2243

@@ -26,7 +47,7 @@
2647

2748
<body class="{% block body_classes %}{% endblock body_classes %}">
2849
{%- block topbar -%}
29-
{%- include "header/topbar.html" -%}
50+
{%- include "header/topbar.html" -%}
3051
{%- endblock topbar -%}
3152

3253
{%- block header %}{% endblock header -%}

0 commit comments

Comments
 (0)