0
0
mirror of https://github.com/PostHog/posthog.git synced 2024-11-24 00:47:50 +01:00

fix(err): structured context, liveness often (#26275)

This commit is contained in:
Oliver Browne 2024-11-19 13:16:47 +02:00 committed by GitHub
parent 40995e96a3
commit ff4ec0aeb4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 67 additions and 28 deletions

View File

@ -68,7 +68,20 @@ pub struct Frame {
// it should never go in clickhouse / be queried over, but we do store it in PG for // it should never go in clickhouse / be queried over, but we do store it in PG for
// use in the frontend // use in the frontend
#[serde(skip)] #[serde(skip)]
pub context: Option<String>, pub context: Option<Context>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct Context {
pub before: Vec<ContextLine>,
pub line: ContextLine,
pub after: Vec<ContextLine>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct ContextLine {
number: u32,
line: String,
} }
impl Frame { impl Frame {
@ -98,3 +111,12 @@ impl Frame {
h.update(self.lang.as_bytes()); h.update(self.lang.as_bytes());
} }
} }
impl ContextLine {
pub fn new(number: u32, line: impl ToString) -> Self {
Self {
number,
line: line.to_string(),
}
}
}

View File

@ -6,7 +6,7 @@ use uuid::Uuid;
use crate::error::UnhandledError; use crate::error::UnhandledError;
use super::Frame; use super::{Context, Frame};
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ErrorTrackingStackFrame { pub struct ErrorTrackingStackFrame {
@ -16,7 +16,7 @@ pub struct ErrorTrackingStackFrame {
pub symbol_set_id: Option<Uuid>, pub symbol_set_id: Option<Uuid>,
pub contents: Frame, pub contents: Frame,
pub resolved: bool, pub resolved: bool,
pub context: Option<String>, pub context: Option<Context>,
} }
impl ErrorTrackingStackFrame { impl ErrorTrackingStackFrame {
@ -26,7 +26,7 @@ impl ErrorTrackingStackFrame {
symbol_set_id: Option<Uuid>, symbol_set_id: Option<Uuid>,
contents: Frame, contents: Frame,
resolved: bool, resolved: bool,
context: Option<String>, context: Option<Context>,
) -> Self { ) -> Self {
Self { Self {
raw_id, raw_id,
@ -61,7 +61,7 @@ impl ErrorTrackingStackFrame {
serde_json::to_value(&self.contents)?, serde_json::to_value(&self.contents)?,
self.resolved, self.resolved,
Uuid::now_v7(), Uuid::now_v7(),
self.context serde_json::to_string(&self.context)?
).execute(e).await?; ).execute(e).await?;
Ok(()) Ok(())
} }
@ -103,9 +103,16 @@ impl ErrorTrackingStackFrame {
// We don't serialise frame contexts on the Frame itself, but save it on the frame record, // We don't serialise frame contexts on the Frame itself, but save it on the frame record,
// and so when we load a frame record we need to patch back up the context onto the frame, // and so when we load a frame record we need to patch back up the context onto the frame,
// since we dropped it when we serialised the frame during saving. // since we dropped it when we serialised the frame during saving.
let mut frame: Frame = serde_json::from_value(found.contents)?; let mut frame: Frame = serde_json::from_value(found.contents)?;
frame.context = found.context.clone(); let context = if let Some(context) = found.context.as_ref() {
// We serialise the frame context as a json string, but it's a structure we have to manually
// deserialise back into the frame.
Some(serde_json::from_str(context)?)
} else {
None
};
frame.context = context.clone();
Ok(Some(Self { Ok(Some(Self {
raw_id: found.raw_id, raw_id: found.raw_id,
@ -114,7 +121,7 @@ impl ErrorTrackingStackFrame {
symbol_set_id: found.symbol_set_id, symbol_set_id: found.symbol_set_id,
contents: frame, contents: frame,
resolved: found.resolved, resolved: found.resolved,
context: found.context, context,
})) }))
} }
} }

View File

@ -1,5 +1,3 @@
use std::cmp::min;
use reqwest::Url; use reqwest::Url;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha512}; use sha2::{Digest, Sha512};
@ -7,7 +5,7 @@ use sourcemap::{SourceMap, Token};
use crate::{ use crate::{
error::{Error, FrameError, JsResolveErr, UnhandledError}, error::{Error, FrameError, JsResolveErr, UnhandledError},
frames::Frame, frames::{Context, ContextLine, Frame},
metric_consts::{FRAME_NOT_RESOLVED, FRAME_RESOLVED}, metric_consts::{FRAME_NOT_RESOLVED, FRAME_RESOLVED},
symbol_store::SymbolCatalog, symbol_store::SymbolCatalog,
}; };
@ -160,28 +158,37 @@ impl From<(&RawJSFrame, JsResolveErr)> for Frame {
} }
} }
fn get_context(token: &Token) -> Option<String> { fn get_context(token: &Token) -> Option<Context> {
let sv = token.get_source_view()?; let sv = token.get_source_view()?;
let token_line = token.get_src_line(); let token_line_num = token.get_src_line();
let start_line = token_line.saturating_sub(5);
let end_line = min(token_line.saturating_add(5) as usize, sv.line_count()) as u32;
// Rough guess on capacity here let token_line = sv.get_line(token_line_num)?;
let mut context = String::with_capacity(((end_line - start_line) * 100) as usize);
for line in start_line..end_line { let mut before = Vec::new();
if let Some(l) = sv.get_line(line) { let mut i = token_line_num;
context.push_str(l); while before.len() < 5 && i > 0 {
context.push('\n'); i -= 1;
if let Some(line) = sv.get_line(i) {
before.push(ContextLine::new(i, line));
}
}
before.reverse();
let mut after = Vec::new();
let mut i = token_line_num;
while after.len() < 5 && i < sv.line_count() as u32 {
i += 1;
if let Some(line) = sv.get_line(i) {
after.push(ContextLine::new(i, line));
} }
} }
if !context.is_empty() { Some(Context {
Some(context) before,
} else { line: ContextLine::new(token_line_num, token_line),
None after,
} })
} }
#[cfg(test)] #[cfg(test)]

View File

@ -108,10 +108,13 @@ async fn process_exception(
// process those groups in-order (but the individual frames in them can still be // process those groups in-order (but the individual frames in them can still be
// thrown at the wall), with some cross-group concurrency. // thrown at the wall), with some cross-group concurrency.
handles.push(tokio::spawn(async move { handles.push(tokio::spawn(async move {
context context.worker_liveness.report_healthy().await;
let res = context
.resolver .resolver
.resolve(&frame, team_id, &context.pool, &context.catalog) .resolve(&frame, team_id, &context.pool, &context.catalog)
.await .await;
context.worker_liveness.report_healthy().await;
res
})); }));
} }