Related: 04_lifecycle.md | 15_services.md | 22_testing.md
1. Severity Levels
| Level | Use Case | Behavior |
| ERROR | Unrecoverable (NaN, solver failure) | Stop simulation, propagate exception |
| WARNING | Recoverable issue (constraint violation, clamp) | Log and continue |
| INFO | Informational (phase change, event) | Log only |
| DEBUG | Development debugging | Log if debug mode enabled |
| TRACE | High-frequency (per-step values) | Log if trace mode enabled |
2. Logging API
ICARUS_ERROR("Solver diverged at t={}", t);
ICARUS_WARN("Clipping thrust to max: {}", max);
ICARUS_INFO("Phase transition: {} -> {}", old, new_phase);
ICARUS_DEBUG("State vector: {}", state);
icarus::Logger::set_level(icarus::LogLevel::INFO);
icarus::Logger::set_output("logs/sim.log");
3. Validation Errors
Signal binding failures, configuration errors, and type mismatches throw during Provision or Stage**—never during **Step.
4. Runtime Debug & Trace Mode
Debug and Trace modes can be enabled **without recompilation**—critical for diagnosing issues in production builds.
4.1 Runtime Configuration
sim.EnableTrace("Vehicle.Aero.*");
sim.EnableTrace("Vehicle.EOM.position");
sim.EnableTrace("GNC.*", TraceLevel::DEBUG);
sim.DisableTrace("Vehicle.Aero.*");
auto traced_signals = sim.GetTracedSignals();
4.2 Configuration File
services:
debug:
enabled: true
# Enable tracing for signal patterns (glob syntax)
trace_signals:
- pattern: "Vehicle.Nav.*"
level: DEBUG
rate_hz: 100 # Downsample high-frequency signals
- pattern: "Vehicle.Aero.alpha"
level: TRACE
rate_hz: 1000
# Enable component-level tracing
trace_components:
- name: "Vehicle.GNC"
level: DEBUG
log_inputs: true
log_outputs: true
log_timing: true # Measure step duration
# Breakpoints (pause simulation when condition met)
breakpoints:
- condition: "Vehicle.Nav.altitude < 0"
action: PAUSE
message: "Vehicle below ground level"
- condition: "Vehicle.EOM.velocity_norm > 1000"
action: LOG_WARNING
message: "Velocity exceeds expected range"
# Output options
output:
console: true
file: "logs/trace_{timestamp}.log"
format: JSON # or TEXT, CSV
5. Implementation
template <typename Scalar>
class TracingBackplane : public Backplane<Scalar> {
std::unordered_set<std::string> traced_signals_;
std::ofstream trace_file_;
public:
void EnableTrace(const std::string& pattern) {
for (const auto& [name, meta] : registry_) {
if (MatchGlob(name, pattern)) {
traced_signals_.insert(name);
}
}
}
void LogTracedSignals(Scalar t) {
if (traced_signals_.empty()) return;
json frame;
frame["t"] = t;
for (const auto& name : traced_signals_) {
frame[name] = GetSignalValue(name);
}
trace_file_ << frame.dump() << "\n";
}
};
6. Component Timing Profiler
class ComponentProfiler {
struct Profile {
std::string name;
double total_time_us = 0;
double max_time_us = 0;
int64_t call_count = 0;
};
std::unordered_map<Component*, Profile> profiles_;
public:
void BeginStep(Component* comp) {
start_times_[comp] = HighResolutionClock::now();
}
void EndStep(Component* comp) {
auto elapsed = HighResolutionClock::now() - start_times_[comp];
auto us = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count();
auto& p = profiles_[comp];
p.total_time_us += us;
p.max_time_us = std::max(p.max_time_us, static_cast<double>(us));
p.call_count++;
}
void PrintReport() {
ICARUS_INFO("=== Component Timing Report ===");
for (const auto& [comp, p] : profiles_) {
double avg = p.total_time_us / p.call_count;
ICARUS_INFO("{}: avg={:.2f}us, max={:.2f}us, calls={}",
p.name, avg, p.max_time_us, p.call_count);
}
}
};
7. Interactive Debugging (Future: Daedalus Integration)
class DebugServer {
public:
void Start(int port = 9999) {
socket_.bind("tcp://*:" + std::to_string(port));
}
};
- Note
- Performance Impact: Tracing adds overhead only for traced signals. When no signals are traced, cost is a single branch per Step (negligible). Component timing profiler adds ~100ns per component when enabled.