Skip to content

Commit c0b830e

Browse files
committed
feat: add structured logging with tracing and subscriber support
- Added `tracing` and `tracing-subscriber` crates for structured logging. - Initialized tracing subscriber in `main()` with `info` level and no timestamps. - Replaced `println!` with `tracing::info!` and `tracing::debug!` where appropriate. - Rewrote tests for clarity using `ensure!()` and better assertions. This improves observability and makes the application better suited for production or Lambda-based debugging.
1 parent db8331e commit c0b830e

File tree

3 files changed

+116
-19
lines changed

3 files changed

+116
-19
lines changed

Cargo.lock

Lines changed: 54 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ lambda_runtime = "0.10"
1010
serde_json = "1.0"
1111
serde = { version = "1.0", features = ["derive"] }
1212
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
13+
tracing = "0.1"
14+
tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] }
15+

src/main.rs

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,20 @@ impl Ord for Action {
2525
fn cmp(&self, other: &Self) -> Ordering {
2626
// ---
2727

28-
if self.next_action_time < other.next_action_time {
29-
return Ordering::Less;
30-
} else if self.next_action_time > other.next_action_time {
31-
return Ordering::Greater;
32-
}
33-
if self.next_action_time < other.next_action_time {
34-
return Ordering::Less;
35-
} else if self.next_action_time > other.next_action_time {
36-
return Ordering::Greater;
37-
}
38-
Ordering::Equal
28+
self.next_action_time.cmp(&other.next_action_time)
3929
}
4030
}
4131

4232
#[tokio::main]
4333
async fn main() -> Result<(), Error> {
4434
// ---
35+
tracing_subscriber::fmt()
36+
.with_env_filter("info") // or customize with RUST_LOG
37+
.with_target(false)
38+
.without_time()
39+
.init();
40+
41+
tracing::info!("Lambda starting...");
4542

4643
let func = service_fn(filter_actions);
4744
lambda_runtime::run(func).await?;
@@ -51,6 +48,11 @@ async fn main() -> Result<(), Error> {
5148
async fn filter_actions(event: LambdaEvent<Value>) -> Result<Value, Error> {
5249
// ---
5350

51+
tracing::info!(
52+
"Processing event with {} actions",
53+
event.payload.as_array().map(|v| v.len()).unwrap_or(0),
54+
);
55+
5456
let now = Utc::now();
5557

5658
let (value, _context) = event.into_parts();
@@ -61,12 +63,15 @@ async fn filter_actions(event: LambdaEvent<Value>) -> Result<Value, Error> {
6163
for action in input {
6264
let days_since_last = (now - action.last_action_time).to_std()?.as_secs() / 86400;
6365
if days_since_last < 7 {
66+
tracing::debug!(%action.entity_id, "Skipping: last action too recent");
6467
continue;
6568
}
6669

6770
if action.next_action_time > now + Duration::days(90) {
71+
tracing::debug!("Skipping (future): {}", action.entity_id);
6872
continue;
6973
}
74+
tracing::info!("Selecting action: {}", action.entity_id);
7075

7176
selected.entry(action.entity_id.clone()).or_insert(action);
7277
}
@@ -75,6 +80,8 @@ async fn filter_actions(event: LambdaEvent<Value>) -> Result<Value, Error> {
7580

7681
actions.sort_by_key(|a| a.priority.clone());
7782

83+
tracing::info!("Returning {} filtered actions", actions.len());
84+
7885
Ok(json!(actions))
7986
}
8087

@@ -153,13 +160,38 @@ mod tests {
153160
},
154161
];
155162

156-
let expected_order = vec!["entity_1", "entity_2"];
157-
158163
let output = process_actions(input);
159-
let ids: Vec<_> = output.iter().map(|a| a.entity_id.as_str()).collect();
160164

161-
assert_eq!(ids, expected_order);
162-
assert_eq!(output[0].priority, Priority::Urgent);
165+
// Verify we have exactly 2 actions after filtering
166+
ensure!(
167+
output.len() == 2,
168+
"Expected 2 actions after filtering, got {}",
169+
output.len()
170+
);
171+
172+
// Verify the complete order: Urgent priority comes first, then Normal
173+
ensure!(
174+
output[0].entity_id == "entity_1",
175+
"Expected first action to be entity_1, got {}",
176+
output[0].entity_id
177+
);
178+
ensure!(
179+
output[0].priority == Priority::Urgent,
180+
"Expected first action to have Urgent priority, got {:?}",
181+
output[0].priority
182+
);
183+
184+
ensure!(
185+
output[1].entity_id == "entity_2",
186+
"Expected second action to be entity_2, got {}",
187+
output[1].entity_id
188+
);
189+
ensure!(
190+
output[1].priority == Priority::Normal,
191+
"Expected second action to have Normal priority, got {:?}",
192+
output[1].priority
193+
);
194+
163195
Ok(())
164196
}
165197

@@ -183,10 +215,18 @@ mod tests {
183215
];
184216

185217
let output = process_actions(input);
186-
assert_eq!(output.len(), 1);
187-
assert_eq!(output[0].entity_id, "duplicate");
218+
ensure!(
219+
output[0].entity_id == "duplicate",
220+
"Expected action to be for entity 'duplicate', got {}",
221+
output[0].entity_id
222+
);
223+
188224
// Currently keeps last seen, so should be Urgent
189-
assert_eq!(output[0].priority, Priority::Urgent);
225+
ensure!(
226+
output[0].priority == Priority::Urgent,
227+
"Expected single remaining item to be Urgent"
228+
);
229+
190230
Ok(())
191231
}
192232

0 commit comments

Comments
 (0)