Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion .github/workflows/ty.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,18 @@ jobs:
if: ${{ env.python_files != '' }}
run: |
echo "Running ty on changed files: ${{ env.python_files }}"
uv run ty check --output-format concise --python-version 3.12 --exclude "**/tests/**" --exclude "**/codegen_tests/**" ${{ env.python_files }}
# Filter out test files for type checking
src_files=()
for file in ${{ env.python_files }}; do
if [[ ! $file == *"/tests/"* && ! $file == *"/codegen_tests/"* ]]; then
src_files+=("${file}")
fi
done

# Only run type checking if there are source files to check
if [ ${#src_files[@]} -gt 0 ]; then
echo "Running ty on source files: ${src_files[*]}"
uv run ty check --output-format concise --python-version 3.12 ${src_files[@]}
else
echo "No source files to check, skipping ty"
fi
119 changes: 53 additions & 66 deletions QUICK_START_LOGGING.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
## ⚡ TL;DR - 3 Step Process

1. **Import the logger**: `from codegen.shared.logging.get_logger import get_logger`
2. **Add `extra={}` to your log calls**: `logger.info("message", extra={"key": "value"})`
3. **Enable telemetry**: `codegen config telemetry enable`
1. **Add `extra={}` to your log calls**: `logger.info("message", extra={"key": "value"})`
1. **Enable telemetry**: `codegen config telemetry enable`

**That's it!** Your logs automatically go to Grafana Cloud when telemetry is enabled.

Expand All @@ -22,11 +22,14 @@ from codegen.shared.logging.get_logger import get_logger
logger = get_logger(__name__)

# Find any existing console.print() or error handling and add:
logger.info("Operation completed", extra={
"operation": "command_name",
"org_id": org_id, # if available
"success": True
})
logger.info(
"Operation completed",
extra={
"operation": "command_name",
"org_id": org_id, # if available
"success": True,
},
)
```

### 2. Test the Integration Right Now
Expand All @@ -38,7 +41,7 @@ codegen config telemetry enable
# 2. Run the demo
python example_enhanced_agent_command.py

# 3. Run any CLI command
# 3. Run any CLI command
codegen agents # or any other command

# 4. Check status
Expand All @@ -48,69 +51,56 @@ codegen config telemetry status
## 📝 Copy-Paste Patterns

### Pattern 1: Operation Start/End

```python
logger = get_logger(__name__)

# At start of function
logger.info("Operation started", extra={
"operation": "command.subcommand",
"user_input": relevant_input
})

# At end of function
logger.info("Operation completed", extra={
"operation": "command.subcommand",
"success": True
})
logger.info("Operation started", extra={"operation": "command.subcommand", "user_input": relevant_input})

# At end of function
logger.info("Operation completed", extra={"operation": "command.subcommand", "success": True})
```

### Pattern 2: Error Handling

```python
try:
# your existing code
pass
except SomeSpecificError as e:
logger.error("Specific error occurred", extra={
"operation": "command.subcommand",
"error_type": "specific_error",
"error_details": str(e)
}, exc_info=True)
logger.error("Specific error occurred", extra={"operation": "command.subcommand", "error_type": "specific_error", "error_details": str(e)}, exc_info=True)
# your existing error handling
```

### Pattern 3: API Calls

```python
# Before API call
logger.info("Making API request", extra={
"operation": "api.request",
"endpoint": "agent/run",
"org_id": org_id
})
logger.info("Making API request", extra={"operation": "api.request", "endpoint": "agent/run", "org_id": org_id})

# After successful API call
logger.info("API request successful", extra={
"operation": "api.request",
"endpoint": "agent/run",
"response_id": response.get("id"),
"status_code": response.status_code
})
logger.info("API request successful", extra={"operation": "api.request", "endpoint": "agent/run", "response_id": response.get("id"), "status_code": response.status_code})
```

## 🎯 What to Log (Priority Order)

### 🔥 High Priority (Add These First)

- **Operation start/end**: When commands begin/complete
- **API calls**: Requests to your backend
- **Authentication events**: Login/logout/token issues
- **Authentication events**: Login/logout/token issues
- **Errors**: Any exception or failure
- **User actions**: Commands run, options selected

### ⭐ Medium Priority

- **Performance**: Duration of operations
- **State changes**: Status updates, configuration changes
- **External tools**: Claude CLI detection, git operations

### 💡 Low Priority (Nice to Have)

- **Debug info**: Internal state, validation steps
- **User behavior**: Which features are used most

Expand All @@ -126,30 +116,30 @@ logger = get_logger(__name__)

def create(prompt: str, org_id: int | None = None, ...):
"""Create a new agent run with the given prompt."""

# ADD: Log start
logger.info("Agent creation started", extra={
"operation": "agent.create",
"org_id": org_id,
"prompt_length": len(prompt)
})

# Your existing code...
try:
response = requests.post(url, headers=headers, json=payload)
agent_run_data = response.json()
# ADD: Log success

# ADD: Log success
logger.info("Agent created successfully", extra={
"operation": "agent.create",
"agent_run_id": agent_run_data.get("id"),
"status": agent_run_data.get("status")
})

except requests.RequestException as e:
# ADD: Log error
logger.error("Agent creation failed", extra={
"operation": "agent.create",
"operation": "agent.create",
"error_type": "api_error",
"error": str(e)
})
Expand All @@ -163,52 +153,49 @@ def create(prompt: str, org_id: int | None = None, ...):

logger = get_logger(__name__)


def _run_claude_interactive(resolved_org_id: int, no_mcp: bool | None) -> None:
session_id = generate_session_id()

# ADD: Log session start
logger.info("Claude session started", extra={
"operation": "claude.session_start",
"session_id": session_id[:8], # Short version for privacy
"org_id": resolved_org_id
})

logger.info(
"Claude session started",
extra={
"operation": "claude.session_start",
"session_id": session_id[:8], # Short version for privacy
"org_id": resolved_org_id,
},
)

# Your existing code...

try:
process = subprocess.Popen([claude_path, "--session-id", session_id])
returncode = process.wait()

# ADD: Log session end
logger.info("Claude session completed", extra={
"operation": "claude.session_complete",
"session_id": session_id[:8],
"exit_code": returncode,
"status": "COMPLETE" if returncode == 0 else "ERROR"
})

logger.info(
"Claude session completed", extra={"operation": "claude.session_complete", "session_id": session_id[:8], "exit_code": returncode, "status": "COMPLETE" if returncode == 0 else "ERROR"}
)

except Exception as e:
# ADD: Log session error
logger.error("Claude session failed", extra={
"operation": "claude.session_error",
"session_id": session_id[:8],
"error": str(e)
})
logger.error("Claude session failed", extra={"operation": "claude.session_error", "session_id": session_id[:8], "error": str(e)})
```

## 🧪 Verification

After making changes:

1. **Run the command**: Execute your enhanced CLI command
2. **Check telemetry status**: `codegen config telemetry status`
3. **Look for logs in Grafana Cloud**: Search for your operation names
4. **Test with telemetry disabled**: `codegen config telemetry disable` - should still work normally
1. **Check telemetry status**: `codegen config telemetry status`
1. **Look for logs in Grafana Cloud**: Search for your operation names
1. **Test with telemetry disabled**: `codegen config telemetry disable` - should still work normally

## 🚀 Progressive Enhancement

**Week 1**: Add basic operation logging to 2-3 commands
**Week 2**: Add error logging to all commands
**Week 2**: Add error logging to all commands
**Week 3**: Add performance metrics and detailed context
**Week 4**: Create Grafana dashboards using the collected data

Expand Down
13 changes: 10 additions & 3 deletions src/codegen/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
# file generated by setuptools-scm
# don't change, don't track in version control

__all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
# Import Agent class for top-level access
from codegen.agents.agent import Agent

__all__ = [
"Agent",
"__version__",
"__version_tuple__",
"version",
"version_tuple",
]

TYPE_CHECKING = False
if TYPE_CHECKING:
from typing import Tuple

VERSION_TUPLE = tuple[int | str, ...]
else:
VERSION_TUPLE = object
Expand Down
1 change: 0 additions & 1 deletion src/codegen/cli/commands/claude/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
"""Claude Code integration commands."""

10 changes: 5 additions & 5 deletions src/codegen/cli/commands/claude/claude_log_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import os
import re
from pathlib import Path
from typing import Dict, Any, Optional
from typing import Any, Optional


def get_hyphenated_cwd() -> str:
Expand Down Expand Up @@ -39,7 +39,7 @@ def get_claude_session_log_path(session_id: str) -> Path:
return log_file


def parse_jsonl_line(line: str) -> Optional[Dict[str, Any]]:
def parse_jsonl_line(line: str) -> Optional[dict[str, Any]]:
"""Parse a single line from a JSONL file.

Args:
Expand Down Expand Up @@ -85,13 +85,13 @@ def read_existing_log_lines(log_path: Path) -> int:
return 0

try:
with open(log_path, "r", encoding="utf-8") as f:
with open(log_path, encoding="utf-8") as f:
return sum(1 for _ in f)
except (OSError, UnicodeDecodeError):
return 0


def validate_log_entry(log_entry: Dict[str, Any]) -> bool:
def validate_log_entry(log_entry: dict[str, Any]) -> bool:
"""Validate a log entry before sending to API.

Args:
Expand All @@ -112,7 +112,7 @@ def validate_log_entry(log_entry: Dict[str, Any]) -> bool:
return True


def format_log_for_api(log_entry: Dict[str, Any]) -> Dict[str, Any]:
def format_log_for_api(log_entry: dict[str, Any]) -> dict[str, Any]:
"""Format a log entry for sending to the API.

Args:
Expand Down
Loading