Skip to content

Commit 58eb733

Browse files
emmaling27Convex, Inc.
authored andcommitted
Add ComponentPath to logs (#27974)
This PR adds `ComponentPath` to function execution logs and log stream events. And it stops skipping logs for components, so they now show up in the dashboard. We can add nicer UI for showing component path later, the screenshot just shows that the logs are no longer skipped. ![Screenshot 2024-07-16 at 2 32 34 PM](https://github.com/user-attachments/assets/b0369d4f-b9c0-4153-bda7-e1668fe7f76c) GitOrigin-RevId: 5c290a46535c4c5b0ccffb57e964278e0dd2f3ac
1 parent 2c204a1 commit 58eb733

File tree

7 files changed

+110
-138
lines changed

7 files changed

+110
-138
lines changed

crates/application/src/function_log.rs

Lines changed: 24 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ use std::{
1515
use common::{
1616
components::{
1717
CanonicalizedComponentFunctionPath,
18-
ComponentFunctionPath,
1918
ComponentPath,
2019
},
2120
errors::{
@@ -74,7 +73,6 @@ use serde_json::{
7473
json,
7574
Value as JsonValue,
7675
};
77-
use sync_types::CanonicalizedUdfPath;
7876
use url::Url;
7977
use usage_tracking::{
8078
AggregatedFunctionUsageStats,
@@ -155,54 +153,9 @@ impl HeapSize for FunctionExecution {
155153
}
156154

157155
impl FunctionExecution {
158-
/// Helper method to construct UDF execution for errors that occurred before
159-
/// execution and thus have no associated runtime information.
160-
pub fn for_error(
161-
udf_path: CanonicalizedUdfPath,
162-
udf_type: UdfType,
163-
unix_timestamp: UnixTimestamp,
164-
error: String,
165-
caller: FunctionCaller,
166-
udf_server_version: Option<semver::Version>,
167-
identity: InertIdentity,
168-
context: ExecutionContext,
169-
) -> Self {
170-
FunctionExecution {
171-
params: UdfParams::Function {
172-
error: Some(JsError::from_message(error)),
173-
identifier: udf_path,
174-
},
175-
unix_timestamp,
176-
execution_timestamp: unix_timestamp,
177-
udf_type,
178-
log_lines: vec![].into(),
179-
tables_touched: WithHeapSize::default(),
180-
cached_result: false,
181-
execution_time: 0.0,
182-
caller,
183-
environment: ModuleEnvironment::Invalid,
184-
syscall_trace: SyscallTrace::new(),
185-
usage_stats: AggregatedFunctionUsageStats::default(),
186-
action_memory_used_mb: match udf_type {
187-
UdfType::Query | UdfType::Mutation => None,
188-
UdfType::Action | UdfType::HttpAction => Some(0),
189-
},
190-
udf_server_version,
191-
identity,
192-
context,
193-
}
194-
}
195-
196156
fn identifier(&self) -> UdfIdentifier {
197157
match &self.params {
198-
UdfParams::Function { identifier, .. } => {
199-
let component = ComponentPath::TODO();
200-
let path = ComponentFunctionPath {
201-
component,
202-
udf_path: identifier.clone().strip(),
203-
};
204-
UdfIdentifier::Function(path.canonicalize())
205-
},
158+
UdfParams::Function { identifier, .. } => UdfIdentifier::Function(identifier.clone()),
206159
UdfParams::Http { identifier, .. } => UdfIdentifier::Http(identifier.clone()),
207160
}
208161
}
@@ -214,9 +167,14 @@ impl FunctionExecution {
214167
} else {
215168
None
216169
};
170+
let component_path = match &self.params {
171+
UdfParams::Function { identifier, .. } => identifier.component.clone(),
172+
UdfParams::Http { .. } => ComponentPath::TODO(),
173+
};
217174

218175
FunctionEventSource {
219-
path: udf_id,
176+
component_path,
177+
udf_path: udf_id,
220178
udf_type: self.udf_type,
221179
module_environment: self.environment,
222180
cached,
@@ -355,8 +313,8 @@ pub enum UdfParams {
355313
// Instead only store the error if there was one. If error is None, the
356314
// function succeeded.
357315
error: Option<JsError>,
358-
/// Path of the UDF that was executed.
359-
identifier: CanonicalizedUdfPath,
316+
/// Path of the component and UDF that was executed.
317+
identifier: CanonicalizedComponentFunctionPath,
360318
},
361319
Http {
362320
result: Result<HttpActionStatusCode, JsError>,
@@ -391,7 +349,7 @@ impl UdfParams {
391349

392350
pub fn identifier_str(&self) -> String {
393351
match self {
394-
Self::Function { identifier, .. } => identifier.clone().strip().to_string(),
352+
Self::Function { identifier, .. } => identifier.udf_path.clone().strip().to_string(),
395353
Self::Http { identifier, .. } => identifier.to_string(),
396354
}
397355
}
@@ -618,28 +576,12 @@ impl<RT: Runtime> FunctionExecutionLog<RT> {
618576
usage: TrackUsage,
619577
context: ExecutionContext,
620578
) {
621-
let udf_path = match outcome.path.clone().into_root_udf_path() {
622-
Ok(udf_path) => udf_path,
623-
Err(_) => {
624-
tracing::warn!(
625-
"Skipping logging non-root query: {:?}:{:?}",
626-
outcome.path.component,
627-
outcome.path.udf_path
628-
);
629-
return;
630-
},
631-
};
632579
let aggregated = match usage {
633580
TrackUsage::Track(usage_tracker) => {
634581
let usage_stats = usage_tracker.gather_user_stats();
635582
let aggregated = usage_stats.aggregate();
636-
let component = ComponentPath::TODO();
637-
let path = ComponentFunctionPath {
638-
component,
639-
udf_path: udf_path.clone().strip(),
640-
};
641583
self.usage_tracking.track_call(
642-
UdfIdentifier::Function(path.canonicalize()),
584+
UdfIdentifier::Function(outcome.path.clone()),
643585
context.execution_id.clone(),
644586
if was_cached {
645587
CallType::CachedQuery
@@ -652,7 +594,7 @@ impl<RT: Runtime> FunctionExecutionLog<RT> {
652594
},
653595
TrackUsage::SystemError => AggregatedFunctionUsageStats::default(),
654596
};
655-
if udf_path.is_system() {
597+
if outcome.path.is_system() {
656598
return;
657599
}
658600
let execution = FunctionExecution {
@@ -661,7 +603,7 @@ impl<RT: Runtime> FunctionExecutionLog<RT> {
661603
Ok(_) => None,
662604
Err(e) => Some(e),
663605
},
664-
identifier: udf_path.clone(),
606+
identifier: outcome.path,
665607
},
666608
unix_timestamp: self.rt.unix_timestamp(),
667609
execution_timestamp: outcome.unix_timestamp,
@@ -760,28 +702,12 @@ impl<RT: Runtime> FunctionExecutionLog<RT> {
760702
usage: TrackUsage,
761703
context: ExecutionContext,
762704
) {
763-
let udf_path = match outcome.path.clone().into_root_udf_path() {
764-
Ok(udf_path) => udf_path,
765-
Err(_) => {
766-
tracing::warn!(
767-
"Skipping logging non-root mutation: {:?}:{:?}",
768-
outcome.path.component,
769-
outcome.path.udf_path
770-
);
771-
return;
772-
},
773-
};
774705
let aggregated = match usage {
775706
TrackUsage::Track(usage_tracker) => {
776707
let usage_stats = usage_tracker.gather_user_stats();
777708
let aggregated = usage_stats.aggregate();
778-
let component = ComponentPath::TODO();
779-
let path = ComponentFunctionPath {
780-
component,
781-
udf_path: udf_path.clone().strip(),
782-
};
783709
self.usage_tracking.track_call(
784-
UdfIdentifier::Function(path.canonicalize()),
710+
UdfIdentifier::Function(outcome.path.clone()),
785711
context.execution_id.clone(),
786712
CallType::Mutation,
787713
usage_stats,
@@ -790,7 +716,7 @@ impl<RT: Runtime> FunctionExecutionLog<RT> {
790716
},
791717
TrackUsage::SystemError => AggregatedFunctionUsageStats::default(),
792718
};
793-
if udf_path.is_system() {
719+
if outcome.path.is_system() {
794720
return;
795721
}
796722
let execution = FunctionExecution {
@@ -799,7 +725,7 @@ impl<RT: Runtime> FunctionExecutionLog<RT> {
799725
Ok(_) => None,
800726
Err(e) => Some(e),
801727
},
802-
identifier: udf_path.clone(),
728+
identifier: outcome.path,
803729
},
804730
unix_timestamp: self.rt.unix_timestamp(),
805731
execution_timestamp: outcome.unix_timestamp,
@@ -860,28 +786,12 @@ impl<RT: Runtime> FunctionExecutionLog<RT> {
860786
fn _log_action(&self, completion: ActionCompletion, usage: TrackUsage) {
861787
let outcome = completion.outcome;
862788
let log_lines = completion.log_lines;
863-
let udf_path = match outcome.path.clone().into_root_udf_path() {
864-
Ok(udf_path) => udf_path,
865-
Err(_) => {
866-
tracing::warn!(
867-
"Skipping logging non-root action: {:?}:{:?}",
868-
outcome.path.component,
869-
outcome.path.udf_path
870-
);
871-
return;
872-
},
873-
};
874789
let aggregated = match usage {
875790
TrackUsage::Track(usage_tracker) => {
876791
let usage_stats = usage_tracker.gather_user_stats();
877792
let aggregated = usage_stats.aggregate();
878-
let component = ComponentPath::TODO();
879-
let path = ComponentFunctionPath {
880-
component,
881-
udf_path: udf_path.clone().strip(),
882-
};
883793
self.usage_tracking.track_call(
884-
UdfIdentifier::Function(path.canonicalize()),
794+
UdfIdentifier::Function(outcome.path.clone()),
885795
completion.context.execution_id.clone(),
886796
CallType::Action {
887797
env: completion.environment,
@@ -894,7 +804,7 @@ impl<RT: Runtime> FunctionExecutionLog<RT> {
894804
},
895805
TrackUsage::SystemError => AggregatedFunctionUsageStats::default(),
896806
};
897-
if udf_path.is_system() {
807+
if outcome.path.is_system() {
898808
return;
899809
}
900810
let execution = FunctionExecution {
@@ -903,7 +813,7 @@ impl<RT: Runtime> FunctionExecutionLog<RT> {
903813
Ok(_) => None,
904814
Err(e) => Some(e),
905815
},
906-
identifier: udf_path,
816+
identifier: outcome.path,
907817
},
908818
unix_timestamp: self.rt.unix_timestamp(),
909819
execution_timestamp: outcome.unix_timestamp,
@@ -932,14 +842,12 @@ impl<RT: Runtime> FunctionExecutionLog<RT> {
932842
log_lines: LogLines,
933843
module_environment: ModuleEnvironment,
934844
) {
935-
let Ok(udf_path) = path.into_root_udf_path() else {
936-
return;
937-
};
938-
if udf_path.is_system() {
845+
if path.is_system() {
939846
return;
940847
}
941848
let event_source = FunctionEventSource {
942-
path: udf_path.strip().to_string(),
849+
component_path: path.component,
850+
udf_path: path.udf_path.strip().to_string(),
943851
udf_type: UdfType::Action,
944852
module_environment,
945853
cached: Some(false),
@@ -1061,7 +969,8 @@ impl<RT: Runtime> FunctionExecutionLog<RT> {
1061969
module_environment: ModuleEnvironment,
1062970
) {
1063971
let event_source = FunctionEventSource {
1064-
path: identifier.to_string(),
972+
component_path: ComponentPath::TODO(),
973+
udf_path: identifier.to_string(),
1065974
udf_type: UdfType::HttpAction,
1066975
module_environment,
1067976
cached: Some(false),

crates/common/src/components/component_path.rs

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ use std::{
66

77
use itertools::Itertools;
88
use sync_types::path::check_valid_path_component;
9-
use value::identifier::Identifier;
9+
use value::{
10+
heap_size::{
11+
HeapSize,
12+
WithHeapSize,
13+
},
14+
identifier::Identifier,
15+
};
1016

1117
// All components under a component have a unique `ComponentName`. For example,
1218
// the root app component may have a waitlist component identified by
@@ -55,19 +61,27 @@ impl Deref for ComponentName {
5561
}
5662
}
5763

64+
impl HeapSize for ComponentName {
65+
fn heap_size(&self) -> usize {
66+
self.0.capacity()
67+
}
68+
}
69+
5870
// Path within the component tree for a particular component. Note that this
5971
// path can potentially change when the component tree changes during a push, so
6072
// we should resolve this path to a `ComponentId` within a transaction
6173
// as soon as possible.
6274
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
6375
#[cfg_attr(any(test, feature = "testing"), derive(proptest_derive::Arbitrary))]
6476
pub struct ComponentPath {
65-
path: Vec<ComponentName>,
77+
path: WithHeapSize<Vec<ComponentName>>,
6678
}
6779

6880
impl ComponentPath {
69-
pub const fn root() -> Self {
70-
Self { path: Vec::new() }
81+
pub fn root() -> Self {
82+
Self {
83+
path: WithHeapSize::default(),
84+
}
7185
}
7286

7387
pub fn is_root(&self) -> bool {
@@ -77,15 +91,15 @@ impl ComponentPath {
7791
/// Use ComponentPath::TODO() when the path should be passed down from a
7892
/// higher layer.
7993
#[allow(non_snake_case)]
80-
pub const fn TODO() -> Self {
94+
pub fn TODO() -> Self {
8195
Self::root()
8296
}
8397

8498
/// Component path to use in tests, representing a user-space component.
8599
/// Ideally this could be changed to an arbitrary path and the tests would
86100
/// still pass.
87101
#[cfg(any(test, feature = "testing"))]
88-
pub const fn test_user() -> Self {
102+
pub fn test_user() -> Self {
89103
Self::root()
90104
}
91105

@@ -120,6 +134,14 @@ impl ComponentPath {
120134
None => Ok(ComponentPath::root()),
121135
}
122136
}
137+
138+
pub fn serialize(self) -> Option<String> {
139+
if self.is_root() {
140+
None
141+
} else {
142+
Some(String::from(self))
143+
}
144+
}
123145
}
124146

125147
impl Deref for ComponentPath {
@@ -132,7 +154,9 @@ impl Deref for ComponentPath {
132154

133155
impl From<Vec<ComponentName>> for ComponentPath {
134156
fn from(path: Vec<ComponentName>) -> Self {
135-
Self { path }
157+
Self {
158+
path: path.into_iter().collect(),
159+
}
136160
}
137161
}
138162

@@ -156,14 +180,20 @@ impl FromStr for ComponentPath {
156180
fn from_str(s: &str) -> Result<Self, Self::Err> {
157181
Ok(Self {
158182
path: if s.is_empty() {
159-
vec![]
183+
WithHeapSize::default()
160184
} else {
161185
s.split('/').map(str::parse).try_collect()?
162186
},
163187
})
164188
}
165189
}
166190

191+
impl HeapSize for ComponentPath {
192+
fn heap_size(&self) -> usize {
193+
self.path.heap_size()
194+
}
195+
}
196+
167197
// Path relative to the `convex` directory for each bundle.
168198
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
169199
pub struct ComponentDefinitionPath(String);

0 commit comments

Comments
 (0)