This document contains internal implementation details, refactoring notes, and development history for contributors and maintainers.
# Clone repository
git clone https://github.com/yourusername/mytrace.git
cd mytrace
# Install dependencies
uv sync
# Install with examples
uv sync --extra examples
# Run in development mode
uv run aitrace --reload --log-level debug
cd aitrace_viewer
# Install dependencies
yarn install
# Development mode
yarn dev
# Build for production
yarn build
# Preview production build
yarn preview
mytrace/
├── aitrace/ # Python tracing library
│ ├── __init__.py # Package exports
│ ├── __main__.py # Entry point
│ ├── buffer.py # BufferedLogger with multiple targets
│ ├── config.py # Configuration system
│ ├── decorators.py # @auto_span decorator
│ ├── logging_config.py # structlog configuration
│ ├── server.py # FastAPI + SQLite backend
│ ├── tracing.py # OpenTelemetry setup
│ └── static/ # Simple web UI
│ ├── index.html
│ ├── style.css
│ └── script.js
├── aitrace_viewer/ # Advanced web viewer
│ ├── src/
│ │ ├── App.tsx # Main application
│ │ ├── components/ # React components
│ │ │ ├── JsonTree.tsx
│ │ │ ├── NodeRow.tsx
│ │ │ ├── TimestampSettings.tsx
│ │ │ └── TreeView.tsx
│ │ ├── lenses/ # Lens configuration
│ │ │ └── lensConfig.ts
│ │ ├── logic/ # Business logic
│ │ │ └── buildSpanTree.ts
│ │ └── utils/ # Utilities
│ │ └── timestampFormat.ts
│ ├── public/
│ │ └── samples/ # Sample JSONL files
│ ├── index.html
│ ├── vite.config.ts
│ └── package.json
├── test/ # Example scripts and tests
│ ├── common.py # Shared test utilities
│ ├── 01_initial.py # Complex e-commerce test
│ ├── 02_simple.py # Simple chatbot example
│ ├── 03_router.py # Router pattern
│ ├── 04_buffered_simple.py # BufferedLogger examples
│ └── 05_target_modes.py # Output target modes demo
├── docs/ # Documentation
│ ├── quickstart.md
│ ├── configuration.md
│ ├── deployment.md
│ ├── viewer.md
│ ├── development.md # This file
│ ├── app/ # Built viewer application
│ └── archive/ # Historical documentation
├── README.md # Main documentation
├── AGENTS.md # Architecture reference
├── CHANGELOG.md # Version history
├── pyproject.toml # Python dependencies
└── uv.lock # Dependency lock file
Version: 0.2.0
Date: 2025-10-19
Early versions had custom buffering code repeated in every test file, leading to:
Created BufferedLogger class as a reusable component:
from aitrace import BufferedLogger
# Simple initialization
buffered = BufferedLogger(target="http://localhost:8000/api/ingest")
log = buffered.logger
# Auto-flush pattern
with buffered.trace_context(tracer, "operation"):
log.info("doing work")
# Automatically flushes on exit
Version: 0.2.0
Date: 2025-10-19
Support for multiple output targets via target parameter or LOG_TRG environment variable:
| Target | Format | Example | Use Case |
|---|---|---|---|
| HTTP | http://... |
http://localhost:8000/api/ingest |
Send to server |
| File | Path | ~/logs/app/<YYYYMMDD_HHMMSS>.jsonl |
Archival |
| Stdout | - |
- |
Container logs |
def _parse_target(self, target: str | None) -> tuple[str, str | None]:
"""Parse target string and determine type"""
if target.startswith(("http://", "https://")):
return ("http", target)
elif target == "-":
return ("stdout", None)
else:
# File path with expansion
expanded = os.path.expanduser(target)
expanded = expanded.replace("<YYYYMMDD_HHMMSS>", self.init_timestamp)
return ("file", expanded)
~/path → /Users/username/path<YYYYMMDD_HHMMSS> → 20251024_103014Version: Unreleased
Date: 2025-10-19
Layered configuration with priority:
Uses configargparse for multi-source configuration:
import configargparse
parser = configargparse.ArgumentParser(
default_config_files=[
"~/.config/aitrace/config.yaml",
"~/.config/aitrace/config.toml"
]
)
parser.add_argument("--port", env_var="AITRACE_PORT", default=8000)
parser.add_argument("--host", env_var="AITRACE_HOST", default="0.0.0.0")
# ... more options
Version: Unreleased
Date: 2025-10-24
Connection failures to trace server caused crashes and unhelpful error messages.
Automatic fallback to HTML export when server is unreachable:
def _flush_http(self):
try:
response = requests.post(self.target_url, json=logs)
return response.json()
except requests.exceptions.ConnectionError as e:
# Fallback to HTML export
html_path = self._export_to_html_fallback(logs)
print(f"⚠️ Cannot connect to server")
print(f"✓ Trace saved to: {html_path}")
return {"ingested": len(logs), "fallback": html_path}
~/tmp/temp-trace/<YYYYMMDD_HHMMSS>.htmlVersion: Unreleased
Date: 2025-10-24
Python objects like UUID, datetime, dataclasses caused TypeError: Object of type X is not JSON serializable.
Custom JSON serializer with support for:
def _json_serializer(obj):
"""Handle non-JSON-serializable types"""
if isinstance(obj, uuid.UUID):
return str(obj)
elif isinstance(obj, (datetime, date)):
return obj.isoformat()
elif isinstance(obj, timedelta):
return obj.total_seconds()
elif isinstance(obj, Decimal):
return float(obj)
elif isinstance(obj, Enum):
return obj.value
elif isinstance(obj, (set, frozenset)):
return list(obj)
elif hasattr(obj, "__dataclass_fields__"):
return dataclasses.asdict(obj)
elif hasattr(obj, "__dict__"):
d = {"__type__": type(obj).__name__}
d.update(obj.__dict__)
return d
else:
return str(obj)
__type__ field for custom objectsVersion: Unreleased
Date: 2025-10-24
Automatically detects “simple” vs “complex” values:
function isSimpleValue(value: any): boolean {
const type = typeof value;
if (value === null || value === undefined) return true;
if (type === "number" || type === "boolean") return true;
if (type === "string" && value.length <= 80) return true;
return false;
}
Simple values rendered as compact chips:
.field-chip {
display: inline-flex;
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
border: 1px solid #ced4da;
background-color: #f8f9fa;
}
Result:
[Message Count: 1] [Model: ChatOpenAI] [Run ID: f3577ee7...]
Complex values rendered as expandable JSON trees with:
Location: test/
common.py)class InspectHandler(BaseCallbackHandler):
"""LangChain callback handler for logging"""
def on_llm_start(self, serialized, prompts, **kwargs):
log.info("llm_start", prompts=prompts, ...)
def init_llm():
"""Initialize LLM with llmlite or direct Anthropic"""
# Supports both llmlite proxy and direct API
def setup_tracing_and_logging(service_name, target=None):
"""Setup tracer and buffered logger"""
tracer = setup_tracing(service_name)
buffered = BufferedLogger(target=target)
return tracer, buffered
| Script | Description | Features |
|---|---|---|
01_initial.py |
Complex e-commerce test | Nested spans, error handling, random failures |
02_simple.py |
Simple chatbot | Basic LangGraph integration |
03_router.py |
Router pattern | Multiple agents, conditional logic |
04_buffered_simple.py |
BufferedLogger examples | Manual flush, auto-flush patterns |
05_target_modes.py |
Output target modes | HTTP, file, stdout demos |
# Start server first
uv run aitrace
# Run individual tests
uv run python test/02_simple.py
uv run python test/03_router.py
# With custom target
export LOG_TRG="~/logs/test/<YYYYMMDD_HHMMSS>.jsonl"
uv run python test/04_buffered_simple.py
# File-based (no server needed)
export LOG_TRG="~/logs/test.jsonl"
uv run python test/02_simple.py
Features:
LOG_TRG support across all test examplesFiles Modified:
aitrace/buffer.py - Added HTML template and fallback logictest/common.py - Updated to respect LOG_TRGtest/01_initial.py, test/04_buffered_simple.py - Removed error handling (now in buffer.py)test/README.md - Updated with LOG_TRG documentationBenefits:
Features:
Files Modified:
aitrace/config.py - New configuration systemaitrace/buffer.py - Added target modes, JSON serializertest/common.py - Extracted shared codedocs/configuration.md - New documentationBreaking Changes:
api_url parameter removed (use target instead)send_logs() function removed (use BufferedLogger.flush())Features:
git checkout -b feature/my-featuretest/: test/0X_feature.pycommon.py utilitiestest/README.mdaitrace_viewer/src/lenses/lensConfig.tsdocs/ folder# Build package
uv build
# Install locally
uv pip install -e .
# Publish to PyPI
uv publish
cd aitrace_viewer
# Build
yarn build
# Output goes to docs/app/
# Deploy to GitHub Pages
git add docs/app
git commit -m "Update viewer build"
git push
pyproject.tomlgit tag v0.X.0git push --tags# Enable debug logging
aitrace --log-level debug --access-log
# Check database
sqlite3 ~/.config/aitrace/logs.db "SELECT COUNT(*) FROM logs;"
# Reset database
rm ~/.config/aitrace/logs.db
aitrace
# Advanced viewer development
cd aitrace_viewer
yarn dev
# Check browser console for errors
# Open DevTools → Console
# Rebuild from scratch
yarn clean
yarn install
yarn build
# Check if server is running
curl http://localhost:8000/api/traces
# Test with file output instead
export LOG_TRG="~/test.jsonl"
uv run python test/02_simple.py
# Check environment
uv run python -c "import aitrace; print(aitrace.__file__)"
# Profile with py-spy
pip install py-spy
py-spy record -o profile.svg -- aitrace
# Memory profiling
pip install memory_profiler
python -m memory_profiler aitrace/server.py
-- Explain query plan
EXPLAIN QUERY PLAN SELECT * FROM logs WHERE trace_id = 'abc';
-- Analyze database
ANALYZE;
-- Check index usage
PRAGMA index_list('logs');
PRAGMA index_info('idx_logs_trace');
// Browser DevTools → Performance
// Record trace tree rendering
// Look for long tasks and optimize