feat(usage): improve usage segment with debug logging, proxy support, and edge case fixes#92
feat(usage): improve usage segment with debug logging, proxy support, and edge case fixes#92neighbads wants to merge 0 commit intoHaleclipse:masterfrom
Conversation
Reviewer's GuideImplements a debug logging facility and CLI flag, enhances the usage segment with richer logging, proxy detection (env + settings), resilient handling of optional seven‑day usage and reset times, adds plain-mode circle icons, and skips usage when a custom ANTHROPIC_BASE_URL is configured. Sequence diagram for usage segment collection with cache, proxy, and debug loggingsequenceDiagram
actor User
participant Cli
participant Logger
participant UsageSegment
participant Credentials
participant Config
participant CacheStore as Cache
participant HttpClient as ureqAgent
participant UsageApi as AnthropicUsageAPI
participant StatusLineGenerator as StatusLine
User->>Cli: run ccline --debug
Cli->>Logger: enable()
Cli->>UsageSegment: collect(input)
UsageSegment->>Logger: log_debug(usage, "check custom ANTHROPIC_BASE_URL")
UsageSegment->>UsageSegment: is_custom_api_base()
alt custom base configured
UsageSegment->>Logger: log_debug(usage, "skipped: custom base")
UsageSegment-->>Cli: None
Cli-->>User: status line without usage
else official API
UsageSegment->>Credentials: get_oauth_token()
alt token found
Credentials->>Logger: log_debug(credentials, "token source details")
Credentials-->>UsageSegment: Some(token)
else token missing
Credentials->>Logger: log_debug(credentials, "no token found")
Credentials-->>UsageSegment: None
UsageSegment->>Logger: log_debug(usage, "failed to get oauth token")
UsageSegment-->>Cli: None
Cli-->>User: status line without usage
end
UsageSegment->>Config: load()
Config-->>UsageSegment: Config
UsageSegment->>UsageSegment: load_cache()
UsageSegment->>CacheStore: read cache file
CacheStore-->>UsageSegment: Option<ApiUsageCache>
alt cache exists
UsageSegment->>Logger: log_debug(usage:cache, "loaded cache")
UsageSegment->>UsageSegment: is_cache_valid(cache, cache_duration)
alt cache valid
UsageSegment->>Logger: log_debug(usage, "using cached data")
UsageSegment-->>Cli: Some(SegmentData)
else cache stale
UsageSegment->>UsageSegment: fetch_api_usage(api_base_url, token, timeout)
UsageSegment->>UsageSegment: get_proxy_url()
UsageSegment->>Logger: log_debug(usage:proxy, "env/settings proxy")
UsageSegment->>HttpClient: build Agent with optional Proxy
HttpClient->>UsageApi: GET /api/usage
UsageApi-->>HttpClient: HTTP 200 + JSON
HttpClient-->>UsageSegment: Response
UsageSegment->>Logger: log_debug(usage:api, "status and body")
alt JSON parses
UsageSegment->>UsageSegment: save_cache(ApiUsageCache)
UsageSegment->>CacheStore: write cache file
CacheStore-->>UsageSegment: ok
UsageSegment-->>Cli: Some(SegmentData)
else parse error
UsageSegment->>Logger: log_debug(usage:api, "deserialization error")
UsageSegment-->>Cli: Some(SegmentData from stale cache)
end
end
else no cache
UsageSegment->>Logger: log_debug(usage:cache, "no cache file found")
UsageSegment->>UsageSegment: fetch_api_usage(api_base_url, token, timeout)
UsageSegment->>UsageSegment: get_proxy_url()
UsageSegment->>HttpClient: build Agent with optional Proxy
HttpClient->>UsageApi: GET /api/usage
UsageApi-->>HttpClient: HTTP response
HttpClient-->>UsageSegment: Response
alt HTTP 200 and valid JSON
UsageSegment->>UsageSegment: save_cache(ApiUsageCache)
UsageSegment-->>Cli: Some(SegmentData)
else API error
UsageSegment->>Logger: log_debug(usage:api, "request failed or parse error")
UsageSegment-->>Cli: None
end
end
Cli->>StatusLine: render_segment(usageConfig, SegmentData)
StatusLine->>StatusLine: choose dynamic_icon_plain or dynamic_icon_nerd
StatusLine-->>User: rendered usage segment
end
Class diagram for updated usage, logging, and CLI structuresclassDiagram
class Cli {
+bool debug
+parse_args() Cli
}
class Logger {
<<module>>
+enable()
+log_debug(tag: &str, msg: &str)
}
class UsageSegment {
+new() UsageSegment
+collect(input: &InputData) Option~SegmentData~
-load_cache() Option~ApiUsageCache~
-save_cache(cache: &ApiUsageCache)
-is_cache_valid(cache: &ApiUsageCache, cache_duration: u64) bool
-get_proxy_url() Option~String~
-fetch_api_usage(api_base_url: &str, token: &str, timeout_secs: u64) Option~ApiUsageResponse~
-is_custom_api_base() bool
-get_circle_icon_nerd(utilization: f64) String
-get_circle_icon_plain(utilization: f64) String
-format_reset_time(reset_time_str: Option~&str~) String
}
class ApiUsageResponse {
+five_hour: UsagePeriod
+seven_day: Option~UsagePeriod~
}
class UsagePeriod {
+utilization: f64
+resets_at: Option~String~
}
class ApiUsageCache {
+five_hour_utilization: f64
+seven_day_utilization: Option~f64~
+resets_at: Option~String~
+cached_at: String
}
class StatusLineGenerator {
-config: AppConfig
+render_segment(config: &SegmentConfig, data: &SegmentData) String
-get_icon(config: &SegmentConfig) String
}
class SegmentData {
+primary: String
+secondary: String
+metadata: HashMap~String, String~
}
class Credentials {
<<module>>
+get_oauth_token() Option~String~
-get_oauth_token_macos() Option~String~
-get_oauth_token_file() Option~String~
-get_credentials_path() Option~PathBuf~
-read_token_from_path(path: &PathBuf) Option~String~
}
class CredentialsFile {
+claude_ai_oauth: Option~ClaudeAiOauth~
}
class ClaudeAiOauth {
+access_token: String
}
class Config {
<<module>>
+load() Result~Config, Error~
+cache_duration_usage: Option~u64~
+style: StyleConfig
}
class StyleConfig {
+mode: StyleMode
}
class StyleMode {
<<enum>>
+Plain
+Other
}
Cli ..> Logger : uses
Cli ..> UsageSegment : drives
UsageSegment ..> ApiUsageResponse : deserializes
UsageSegment ..> UsagePeriod
UsageSegment ..> ApiUsageCache
UsageSegment ..> Logger : log_debug
UsageSegment ..> Credentials : get_oauth_token
UsageSegment ..> Config : load
StatusLineGenerator ..> SegmentData
StatusLineGenerator ..> StyleMode
Credentials ..> CredentialsFile
CredentialsFile ..> ClaudeAiOauth
Credentials ..> Logger : log_debug
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- The proxy resolution logic in
get_proxy_urlonly checksHTTPS_PROXY/HTTP_PROXY(and lowercase variants) but notALL_PROXY, which is mentioned in the PR description; consider includingALL_PROXY/all_proxyin the lookup order for consistency with the documented behavior. - The new
is_custom_api_baseand proxy helpers both reimplement home-directory resolution and~/.claude/settings.jsonparsing; factoring this into a shared utility would reduce duplication and keep future config changes in one place.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The proxy resolution logic in `get_proxy_url` only checks `HTTPS_PROXY`/`HTTP_PROXY` (and lowercase variants) but not `ALL_PROXY`, which is mentioned in the PR description; consider including `ALL_PROXY`/`all_proxy` in the lookup order for consistency with the documented behavior.
- The new `is_custom_api_base` and proxy helpers both reimplement home-directory resolution and `~/.claude/settings.json` parsing; factoring this into a shared utility would reduce duplication and keep future config changes in one place.
## Individual Comments
### Comment 1
<location path="src/core/segments/usage.rs" line_range="161-164" />
<code_context>
- fn get_proxy_from_settings() -> Option<String> {
+ fn get_proxy_url() -> Option<String> {
+ // 1. Environment variables first (HTTPS_PROXY, HTTP_PROXY, lowercase variants)
+ for var in &["HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy"] {
+ if let Ok(val) = std::env::var(var) {
+ if !val.is_empty() {
+ log_debug("usage:proxy", &format!("using env var {}={}", var, val));
+ return Some(val);
+ }
</code_context>
<issue_to_address>
**🚨 issue (security):** Avoid logging proxy URLs directly, as they may contain embedded credentials.
Since env-based proxy URLs can be of the form `http://user:pass@host:port`, this log line may write credentials to disk in plaintext. Instead, either log only that a proxy is being used (and which env var supplied it), or sanitize the URL before logging (e.g., mask the password).
</issue_to_address>
### Comment 2
<location path="src/core/segments/usage.rs" line_range="262-264" />
<code_context>
+ /// When set, the user is using a non-official API that lacks the usage endpoint.
+ fn is_custom_api_base() -> bool {
+ // 1. Check environment variables
+ if let Ok(val) = std::env::var("ANTHROPIC_BASE_URL") {
+ if !val.is_empty() {
+ log_debug("usage", &format!("ANTHROPIC_BASE_URL env set: {}", val));
+ return true;
+ }
</code_context>
<issue_to_address>
**🚨 suggestion (security):** Consider redacting or shortening logged ANTHROPIC_BASE_URL values to avoid leaking potentially sensitive endpoints.
Even in a debug-only path, logging the full `ANTHROPIC_BASE_URL` (from env or settings.json) can reveal internal hostnames or infrastructure details. Prefer logging only that a custom base URL is set (optionally just the hostname) or redact path/query components.
Suggested implementation:
```rust
// Avoid logging the full ANTHROPIC_BASE_URL to prevent leaking internal endpoints.
log_debug("usage", "ANTHROPIC_BASE_URL env set (custom base URL configured via env)");
```
There may be similar logging for a custom base URL loaded from `settings.json` or other configuration sources elsewhere in `is_custom_api_base()` or related functions. Any log lines that include the full base URL value should be updated similarly to:
- Avoid printing the entire URL, especially path/query parts.
- Prefer a generic message (e.g., "custom base URL configured via settings") or at most log a heavily redacted form (e.g., just the scheme and hostname if truly needed).
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
src/core/segments/usage.rs
Outdated
| for var in &["HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy"] { | ||
| if let Ok(val) = std::env::var(var) { | ||
| if !val.is_empty() { | ||
| log_debug("usage:proxy", &format!("using env var {}={}", var, val)); |
There was a problem hiding this comment.
🚨 issue (security): Avoid logging proxy URLs directly, as they may contain embedded credentials.
Since env-based proxy URLs can be of the form http://user:pass@host:port, this log line may write credentials to disk in plaintext. Instead, either log only that a proxy is being used (and which env var supplied it), or sanitize the URL before logging (e.g., mask the password).
src/core/segments/usage.rs
Outdated
| if let Ok(val) = std::env::var("ANTHROPIC_BASE_URL") { | ||
| if !val.is_empty() { | ||
| log_debug("usage", &format!("ANTHROPIC_BASE_URL env set: {}", val)); |
There was a problem hiding this comment.
🚨 suggestion (security): Consider redacting or shortening logged ANTHROPIC_BASE_URL values to avoid leaking potentially sensitive endpoints.
Even in a debug-only path, logging the full ANTHROPIC_BASE_URL (from env or settings.json) can reveal internal hostnames or infrastructure details. Prefer logging only that a custom base URL is set (optionally just the hostname) or redact path/query components.
Suggested implementation:
// Avoid logging the full ANTHROPIC_BASE_URL to prevent leaking internal endpoints.
log_debug("usage", "ANTHROPIC_BASE_URL env set (custom base URL configured via env)");There may be similar logging for a custom base URL loaded from settings.json or other configuration sources elsewhere in is_custom_api_base() or related functions. Any log lines that include the full base URL value should be updated similarly to:
- Avoid printing the entire URL, especially path/query parts.
- Prefer a generic message (e.g., "custom base URL configured via settings") or at most log a heavily redacted form (e.g., just the scheme and hostname if truly needed).
Summary
--debugflag for logging support across the applicationHTTP_PROXY,HTTPS_PROXY,ALL_PROXY)ANTHROPIC_BASE_URLis set (avoids errors with non-standard API endpoints)Commits
2898c7bfeat: add --debug logging supportb846f94fix(usage): support five-hour only API response formatca33bd8feat(usage): add HTTP proxy support via environment variablesfae50fafix(usage): improve reset time display formatd49b85efix(usage): add plain mode circle icons and debug loggingebb8765fix(usage): skip usage segment when custom ANTHROPIC_BASE_URL is setTest plan
--debugflag enables logging outputANTHROPIC_BASE_URLis setSummary by Sourcery
Add configurable debug logging and improve the usage status segment’s robustness and display behavior.
New Features:
Bug Fixes:
Enhancements:
Tests:
Chores: