Skip to content

Commit 0bbedda

Browse files
gouravjshahclaude
andauthored
fix: aofctl serve now produces visible startup output (#93)
Changed critical startup messages from tracing macros to println/eprintln so they are always visible regardless of log level. The CLI's default log level is "error", which was suppressing all info-level startup messages. - Server bind address, registered platforms, loaded agents/flows now shown - Error messages use stderr for proper output separation - Updated docs with expected startup output section 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.5 <[email protected]>
1 parent 08751c2 commit 0bbedda

File tree

3 files changed

+72
-47
lines changed

3 files changed

+72
-47
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Fixed
11+
- `aofctl serve` now produces visible startup output
12+
- Changed from tracing (default level: error) to println for critical startup messages
13+
- Users can now see server bind address, registered platforms, loaded agents/flows
14+
- Error messages use stderr for proper output separation
15+
1016
## [0.3.1-beta] - 2025-12-26
1117

1218
### Added

crates/aofctl/src/commands/serve.rs

Lines changed: 46 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ use aof_triggers::{
2323
};
2424
use serde::{Deserialize, Serialize};
2525
use tokio::sync::RwLock;
26-
use tracing::{info, warn, error};
2726

2827
/// Server configuration loaded from YAML
2928
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -309,7 +308,7 @@ pub async fn execute(
309308
) -> anyhow::Result<()> {
310309
// Load configuration
311310
let config = if let Some(config_path) = config_file {
312-
info!("Loading configuration from: {}", config_path);
311+
println!("Loading configuration from: {}", config_path);
313312
let content = std::fs::read_to_string(config_path)?;
314313
serde_yaml::from_str::<ServeConfig>(&content)?
315314
} else {
@@ -351,8 +350,8 @@ pub async fn execute(
351350
.parse()
352351
.map_err(|e| anyhow::anyhow!("Invalid bind address: {}", e))?;
353352

354-
info!("Starting AOF Trigger Server");
355-
info!(" Bind address: {}", bind_addr);
353+
println!("Starting AOF Trigger Server");
354+
println!(" Bind address: {}", bind_addr);
356355

357356
// Create runtime orchestrator
358357
let orchestrator = Arc::new(
@@ -370,7 +369,7 @@ pub async fn execute(
370369
};
371370

372371
if let Some(ref agent) = config.spec.runtime.default_agent {
373-
info!(" Default agent for natural language: {}", agent);
372+
println!(" Default agent for natural language: {}", agent);
374373
}
375374

376375
// Create trigger handler
@@ -408,15 +407,15 @@ pub async fn execute(
408407
match SlackPlatform::new_with_auto_detection(platform_config).await {
409408
Ok(platform) => {
410409
handler.register_platform(Arc::new(platform));
411-
info!(" Registered platform: slack");
410+
println!(" Registered platform: slack");
412411
platforms_registered += 1;
413412
}
414413
Err(e) => {
415-
warn!(" Failed to create Slack platform: {}", e);
414+
eprintln!(" Failed to create Slack platform: {}", e);
416415
}
417416
}
418417
} else {
419-
warn!(" Slack enabled but missing bot_token or signing_secret");
418+
eprintln!(" Slack enabled but missing bot_token or signing_secret");
420419
}
421420
}
422421
}
@@ -438,10 +437,10 @@ pub async fn execute(
438437
};
439438
let platform = Arc::new(DiscordPlatform::new(platform_config));
440439
handler.register_platform(platform);
441-
info!(" Registered platform: discord");
440+
println!(" Registered platform: discord");
442441
platforms_registered += 1;
443442
} else {
444-
warn!(" Discord enabled but missing bot_token");
443+
eprintln!(" Discord enabled but missing bot_token");
445444
}
446445
}
447446
}
@@ -466,15 +465,15 @@ pub async fn execute(
466465
match TelegramPlatform::new(platform_config) {
467466
Ok(platform) => {
468467
handler.register_platform(Arc::new(platform));
469-
info!(" Registered platform: telegram");
468+
println!(" Registered platform: telegram");
470469
platforms_registered += 1;
471470
}
472471
Err(e) => {
473-
warn!(" Failed to create Telegram platform: {}", e);
472+
eprintln!(" Failed to create Telegram platform: {}", e);
474473
}
475474
}
476475
} else {
477-
warn!(" Telegram enabled but missing bot_token");
476+
eprintln!(" Telegram enabled but missing bot_token");
478477
}
479478
}
480479
}
@@ -500,15 +499,15 @@ pub async fn execute(
500499
match WhatsAppPlatform::new(platform_config) {
501500
Ok(platform) => {
502501
handler.register_platform(Arc::new(platform));
503-
info!(" Registered platform: whatsapp");
502+
println!(" Registered platform: whatsapp");
504503
platforms_registered += 1;
505504
}
506505
Err(e) => {
507-
warn!(" Failed to create WhatsApp platform: {}", e);
506+
eprintln!(" Failed to create WhatsApp platform: {}", e);
508507
}
509508
}
510509
} else {
511-
warn!(" WhatsApp enabled but missing access_token");
510+
eprintln!(" WhatsApp enabled but missing access_token");
512511
}
513512
}
514513
}
@@ -519,13 +518,13 @@ pub async fn execute(
519518
.or_else(|| config.spec.triggers.directory.clone());
520519

521520
if let Some(ref triggers_path) = triggers_dir_path {
522-
info!("Loading Triggers from: {}", triggers_path.display());
521+
println!("Loading Triggers from: {}", triggers_path.display());
523522
let mut trigger_registry = TriggerRegistry::new();
524523

525524
match trigger_registry.load_directory(triggers_path) {
526525
Ok(count) => {
527526
if count > 0 {
528-
info!(" Loaded {} triggers: {:?}", count, trigger_registry.names());
527+
println!(" Loaded {} triggers: {:?}", count, trigger_registry.names());
529528

530529
// Register platforms for each trigger type
531530
for trigger in trigger_registry.get_all() {
@@ -539,7 +538,7 @@ pub async fn execute(
539538
.unwrap_or_default();
540539

541540
if token.is_empty() {
542-
warn!(" GitHub trigger '{}': GITHUB_TOKEN not set, API features disabled", trigger.name());
541+
eprintln!(" GitHub trigger '{}': GITHUB_TOKEN not set, API features disabled", trigger.name());
543542
}
544543

545544
let github_config = GitHubConfig {
@@ -558,15 +557,15 @@ pub async fn execute(
558557
match GitHubPlatform::new(github_config) {
559558
Ok(platform) => {
560559
handler.register_platform(Arc::new(platform));
561-
info!(" Registered platform: github (from trigger '{}')", trigger.name());
560+
println!(" Registered platform: github (from trigger '{}')", trigger.name());
562561
platforms_registered += 1;
563562
}
564563
Err(e) => {
565-
warn!(" Failed to create GitHub platform: {}", e);
564+
eprintln!(" Failed to create GitHub platform: {}", e);
566565
}
567566
}
568567
} else {
569-
warn!(" GitHub trigger '{}' missing webhook_secret", trigger.name());
568+
eprintln!(" GitHub trigger '{}' missing webhook_secret", trigger.name());
570569
}
571570
}
572571
// Other trigger types use platforms registered from config
@@ -588,32 +587,32 @@ pub async fn execute(
588587
handler.register_command_binding(cmd_name.clone(), handler_binding);
589588

590589
if let Some(ref agent) = binding.agent {
591-
info!(" Registered command '{}' -> agent '{}'", cmd, agent);
590+
println!(" Registered command '{}' -> agent '{}'", cmd, agent);
592591
} else if let Some(ref fleet) = binding.fleet {
593-
info!(" Registered command '{}' -> fleet '{}'", cmd, fleet);
592+
println!(" Registered command '{}' -> fleet '{}'", cmd, fleet);
594593
} else if let Some(ref flow) = binding.flow {
595-
info!(" Registered command '{}' -> flow '{}'", cmd, flow);
594+
println!(" Registered command '{}' -> flow '{}'", cmd, flow);
596595
}
597596
}
598597

599598
// Set default agent if specified in trigger
600599
if let Some(ref default_agent) = trigger.spec.default_agent {
601-
info!(" Default agent for trigger '{}': {}", trigger.name(), default_agent);
600+
println!(" Default agent for trigger '{}': {}", trigger.name(), default_agent);
602601
}
603602
}
604603
} else {
605-
info!(" No Trigger files found in {}", triggers_path.display());
604+
println!(" No Trigger files found in {}", triggers_path.display());
606605
}
607606
}
608607
Err(e) => {
609-
warn!(" Failed to load triggers from {}: {}", triggers_path.display(), e);
608+
eprintln!(" Failed to load triggers from {}: {}", triggers_path.display(), e);
610609
}
611610
}
612611
}
613612

614613
if platforms_registered == 0 {
615-
warn!("No platforms registered! Server will start but won't process any webhooks.");
616-
warn!("Configure platforms in your config file or set environment variables.");
614+
eprintln!("Warning: No platforms registered! Server will start but won't process any webhooks.");
615+
eprintln!("Configure platforms in your config file or set environment variables.");
617616
}
618617

619618
// Load AgentFlows and set up FlowRouter
@@ -626,7 +625,7 @@ pub async fn execute(
626625

627626
if config.spec.flows.enabled {
628627
if let Some(ref flows_path) = flows_dir_path {
629-
info!("Loading AgentFlows from: {}", flows_path.display());
628+
println!("Loading AgentFlows from: {}", flows_path.display());
630629

631630
match FlowRegistry::from_directory(flows_path).await {
632631
Ok(registry) => {
@@ -653,27 +652,27 @@ pub async fn execute(
653652
if let Some(ref ap) = agents_path {
654653
match handler.load_agents_from_directory(ap).await {
655654
Ok(count) => {
656-
info!(" Pre-loaded {} agents from {:?}", count, ap);
655+
println!(" Pre-loaded {} agents from {:?}", count, ap);
657656
agents_loaded = true;
658657
}
659-
Err(e) => warn!(" Failed to pre-load agents: {}", e),
658+
Err(e) => eprintln!(" Failed to pre-load agents: {}", e),
660659
}
661660
}
662661

663-
info!(" Loaded {} AgentFlows: {:?}", flow_count, flow_names);
662+
println!(" Loaded {} AgentFlows: {:?}", flow_count, flow_names);
664663
} else {
665-
info!(" No AgentFlow files found in {}", flows_path.display());
664+
println!(" No AgentFlow files found in {}", flows_path.display());
666665
}
667666
}
668667
Err(e) => {
669-
warn!(" Failed to load AgentFlows from {}: {}", flows_path.display(), e);
668+
eprintln!(" Failed to load AgentFlows from {}: {}", flows_path.display(), e);
670669
}
671670
}
672671
} else {
673-
info!(" No flows directory configured - using default agent routing");
672+
println!(" No flows directory configured - using default agent routing");
674673
}
675674
} else {
676-
info!(" Flow-based routing disabled");
675+
println!(" Flow-based routing disabled");
677676
}
678677

679678
// Pre-load agents from directory if not already done
@@ -687,8 +686,8 @@ pub async fn execute(
687686
let runtime = Arc::new(RwLock::new(Runtime::new()));
688687
handler.set_runtime(runtime);
689688
match handler.load_agents_from_directory(ap).await {
690-
Ok(count) => info!(" Pre-loaded {} agents from {:?}", count, ap),
691-
Err(e) => warn!(" Failed to pre-load agents: {}", e),
689+
Ok(count) => println!(" Pre-loaded {} agents from {:?}", count, ap),
690+
Err(e) => eprintln!(" Failed to pre-load agents: {}", e),
692691
}
693692
}
694693
}
@@ -704,28 +703,28 @@ pub async fn execute(
704703
// Create and start server
705704
let server = TriggerServer::with_config(Arc::new(handler), server_config);
706705

707-
info!("Server starting...");
708-
info!(" Health check: http://{}/health", bind_addr);
709-
info!(" Webhook endpoint: http://{}/webhook/{{platform}}", bind_addr);
710-
info!("Press Ctrl+C to stop");
706+
println!("Server starting...");
707+
println!(" Health check: http://{}/health", bind_addr);
708+
println!(" Webhook endpoint: http://{}/webhook/{{platform}}", bind_addr);
709+
println!("Press Ctrl+C to stop");
711710

712711
// Handle graceful shutdown
713712
let shutdown_signal = async {
714713
tokio::signal::ctrl_c()
715714
.await
716715
.expect("Failed to install Ctrl+C handler");
717-
info!("Shutdown signal received, stopping server...");
716+
println!("\nShutdown signal received, stopping server...");
718717
};
719718

720719
tokio::select! {
721720
result = server.serve() => {
722721
if let Err(e) = result {
723-
error!("Server error: {}", e);
722+
eprintln!("Server error: {}", e);
724723
return Err(anyhow::anyhow!("Server error: {}", e));
725724
}
726725
}
727726
_ = shutdown_signal => {
728-
info!("Server stopped gracefully");
727+
println!("Server stopped gracefully");
729728
}
730729
}
731730

docs/reference/daemon-config.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,26 @@ aofctl serve \
421421
aofctl serve --config daemon-config.yaml --port 3000
422422
```
423423

424+
### Expected Startup Output
425+
426+
When the server starts successfully, you'll see:
427+
428+
```
429+
Loading configuration from: daemon-config.yaml
430+
Starting AOF Trigger Server
431+
Bind address: 0.0.0.0:3000
432+
Default agent for natural language: devops
433+
Registered platform: slack
434+
Registered platform: telegram
435+
Loading AgentFlows from: ./flows/
436+
Pre-loaded 19 agents from "./agents/"
437+
Loaded 3 AgentFlows: ["k8s-health-check", "docker-troubleshoot", "data-pipeline"]
438+
Server starting...
439+
Health check: http://0.0.0.0:3000/health
440+
Webhook endpoint: http://0.0.0.0:3000/webhook/{platform}
441+
Press Ctrl+C to stop
442+
```
443+
424444
---
425445

426446
## Platform Safety

0 commit comments

Comments
 (0)