feat(observability): Add metrics, logging, and sync-run core modules
Introduce the foundational observability layer for the sync pipeline: - MetricsLayer: Custom tracing subscriber layer that captures span timing and structured fields, materializing them into a hierarchical Vec<StageTiming> tree for robot-mode performance data output - logging: Dual-layer subscriber infrastructure with configurable stderr verbosity (-v/-vv/-vvv) and always-on JSON file logging with daily rotation and configurable retention (default 30 days) - SyncRunRecorder: Compile-time enforced lifecycle recorder for sync_runs table (start -> succeed|fail), with correlation IDs and aggregate counts - LoggingConfig: New config section for log_dir, retention_days, and file_logging toggle - get_log_dir(): Path helper for log directory resolution - is_permanent_api_error(): Distinguish retryable vs permanent API failures (only 404 is truly permanent; 403/auth errors may be environmental) Database changes: - Migration 013: Add resource_events_synced_for_updated_at watermark columns to issues and merge_requests tables for incremental resource event sync - Migration 014: Enrich sync_runs with run_id correlation ID, aggregate counts (total_items_processed, total_errors), and run_id index - Wrap file-based migrations in savepoints for rollback safety Dependencies: Add uuid (run_id generation), tracing-appender (file logging) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -59,7 +59,7 @@ impl ErrorCode {
|
||||
pub fn exit_code(&self) -> i32 {
|
||||
match self {
|
||||
Self::InternalError => 1,
|
||||
Self::ConfigNotFound => 20,
|
||||
Self::ConfigNotFound => 2,
|
||||
Self::ConfigInvalid => 3,
|
||||
Self::TokenNotSet => 4,
|
||||
Self::GitLabAuthFailed => 5,
|
||||
@@ -240,6 +240,15 @@ impl LoreError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this error represents a permanent API failure that should not be retried.
|
||||
///
|
||||
/// Only 404 (not found) is truly permanent: the resource doesn't exist and never will.
|
||||
/// 403 and auth errors are NOT permanent — they may be environmental (VPN down,
|
||||
/// token rotation, temporary restrictions) and should be retried with backoff.
|
||||
pub fn is_permanent_api_error(&self) -> bool {
|
||||
matches!(self, Self::GitLabNotFound { .. })
|
||||
}
|
||||
|
||||
/// Get the exit code for this error.
|
||||
pub fn exit_code(&self) -> i32 {
|
||||
self.code().exit_code()
|
||||
|
||||
Reference in New Issue
Block a user