# CompileLog Module Reference
**Module:** `CompileLog`
**File:** `lib/compile_log.ex`
**Database:** `data/log.sqlite`
**Process Name:** `:compile_log_db`
---
## Table of Contents
- [Overview](#overview)
- [Features](#features)
- [Dependencies](#dependencies)
- [Database Schema](#database-schema)
- [Functions](#functions)
- [setup_database/1](#setup_database1)
- [log/2](#log2)
- [get_recent/2](#get_recent2)
- [get_by_file/2](#get_by_file2)
- [get_git_diff/1](#get_git_diff1)
- [cleanup_old_logs/2](#cleanup_old_logs2)
- [stats/1](#stats1)
- [Examples](#examples)
- [Integration](#integration)
- [Performance](#performance)
- [Troubleshooting](#troubleshooting)
---
## Overview
`CompileLog` is an automatic compilation logging system that captures Elixir file compilations with git diff information. It integrates seamlessly with `AutoCompile` to create a persistent audit trail of all code changes during development.
### Purpose
- Track compilation events automatically
- Capture git diffs at moment of compilation
- Provide queryable history of file changes
- Enable development workflow analysis
- Support debugging and code review
### Automatic Operation
Once configured, CompileLog operates transparently:
1. Developer saves an `.ex` file
2. `AutoCompile` compiles the file
3. `CompileLog.log/2` is called automatically
4. Git diff is captured and stored
5. Entry is persisted to SQLite database
### Storage
All logs are stored in `data/log.sqlite` with indexed access for fast queries.
---
## Features
### ✅ Automatic Logging
Triggered on every `.ex` file compilation without manual intervention.
### ✅ Git Diff Capture
Stores `git diff -U0` output with minimal context for compact storage.
### ✅ Millisecond Precision
Timestamps accurate to the millisecond for precise chronological ordering.
### ✅ File History
Query complete compilation history for any file.
### ✅ Statistics
Aggregate data showing most-compiled files and development patterns.
### ✅ Cleanup Utilities
Remove old logs based on age to manage database size.
### ✅ SQLite Storage
Persistent, indexed storage with efficient queries.
### ✅ Zero Configuration
Works automatically once installed, no manual setup required.
---
## Dependencies
### Required Modules
| Module | Purpose | Used For |
|--------|---------|----------|
| `Sqler` | SQLite database wrapper | All database operations |
| `Logger` | Elixir logging | Debug and error messages |
| `System` | System commands | Git diff execution, timestamps |
### External Dependencies
| Dependency | Required | Purpose | Fallback |
|-----------|----------|---------|----------|
| Git | Optional | Capture git diff | Returns `nil` if unavailable |
| SQLite | Required | Database storage | Application won't start without it |
### Application Configuration
Configured in `lib/mcp/application.ex`:
```elixir
children = [
{Sqler, name: "log", register: :compile_log_db},
{AutoCompile, ["lib/"]},
# ...
]
# In start/2 callback
CompileLog.setup_database(:compile_log_db)
```
---
## Database Schema
### Table: auto_compile
Complete schema definition:
```sql
CREATE TABLE IF NOT EXISTS auto_compile (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
time INTEGER NOT NULL,
git_diff TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_auto_compile_name
ON auto_compile(name);
CREATE INDEX IF NOT EXISTS idx_auto_compile_time
ON auto_compile(time);
```
### Column Definitions
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| `id` | INTEGER | PRIMARY KEY AUTOINCREMENT | Auto-generated unique identifier |
| `name` | TEXT | NOT NULL | File path relative to project root |
| `time` | INTEGER | NOT NULL | Unix timestamp in milliseconds |
| `git_diff` | TEXT | NULL | Output of `git diff -U0 ` |
| `created_at` | INTEGER | NOT NULL | Record creation timestamp |
| `updated_at` | INTEGER | NOT NULL | Record update timestamp (for Sqler) |
### Indexes
**idx_auto_compile_name**
- Column: `name`
- Purpose: Fast file-specific queries
- Used by: `get_by_file/2`, stats aggregation
**idx_auto_compile_time**
- Column: `time`
- Purpose: Fast chronological queries
- Used by: `get_recent/2`, time range queries
### Database File
```
Location: data/log.sqlite
Created: Automatically on first startup
Size: ~1KB per log entry (varies with diff size)
```
---
## Functions
### setup_database/1
#### Description
Initializes the database schema by creating the `auto_compile` table and indexes if they don't exist.
#### Spec
```elixir
@spec setup_database(atom()) :: :ok
```
#### Parameters
**db** :: `atom()` (default: `:compile_log_db`)
- Database process name
- Must be a registered Sqler process
- Default uses the standard compile log database
#### Returns
**:ok** :: Always returns success
- Tables and indexes are created with `IF NOT EXISTS`
- Safe to call multiple times
- Logs "CompileLog database initialized" on completion
#### Examples
```elixir
# Default database
CompileLog.setup_database()
#=> :ok
# Custom database
CompileLog.setup_database(:my_log_db)
#=> :ok
# Called automatically on app startup
# No need to call manually
```
#### Implementation Details
Executes three SQL statements:
1. Creates `auto_compile` table
2. Creates `idx_auto_compile_name` index
3. Creates `idx_auto_compile_time` index
Uses `Sqler.sql/2` for execution.
---
### log/2
#### Description
Logs a compilation event by capturing the current timestamp, getting git diff, and inserting a record into the database.
#### Spec
```elixir
@spec log(String.t(), atom()) :: {:ok, integer()} | {:error, any()}
```
#### Parameters
**file_path** :: `String.t()` (required)
- Path to the compiled file
- Should be relative to project root
- Example: `"lib/user.ex"`
**db** :: `atom()` (default: `:compile_log_db`)
- Database process name
- Must be a registered Sqler process
#### Returns
**{:ok, log_id}** :: Success
- `log_id` is the auto-generated database ID
- ID is a millisecond timestamp (monotonically increasing)
**{:error, reason}** :: Failure
- Database insert failed
- Returns error reason from Sqler
#### Examples
```elixir
# Basic usage (called automatically by AutoCompile)
CompileLog.log("lib/user.ex")
#=> {:ok, 1738350123456}
# Manual logging with custom database
CompileLog.log("lib/my_module.ex", :custom_db)
#=> {:ok, 1738350234567}
# Error case
CompileLog.log("invalid/path.ex")
#=> {:error, "Insert failed - no rows affected"}
```
#### Implementation
```elixir
def log(file_path, db \\ @db_name) do
now = System.system_time(:millisecond)
git_diff = get_git_diff(file_path)
log_entry = %{
name: file_path,
time: now,
git_diff: git_diff,
created_at: now
}
# Sqler auto-generates id and updated_at
Sqler.insert(db, "auto_compile", log_entry)
end
```
#### Side Effects
- Logs debug message on success
- Logs error message on failure
- Executes `git diff -U0 ` command
---
### get_recent/2
#### Description
Retrieves the most recent compilation logs, ordered by timestamp (newest first).
#### Spec
```elixir
@spec get_recent(non_neg_integer(), atom()) :: list()
```
#### Parameters
**limit** :: `non_neg_integer()` (default: `20`)
- Maximum number of logs to retrieve
- Must be non-negative
- No upper bound (use reasonable values)
**db** :: `atom()` (default: `:compile_log_db`)
- Database process name
#### Returns
**list()** :: List of log entries
- Each entry: `[name, time]`
- Ordered by `time DESC` (newest first)
- Empty list if no logs exist
**Note:** Currently returns simplified format. Full format would be `[id, name, time, git_diff, created_at, updated_at]`.
#### Examples
```elixir
# Get last 10 compilations
CompileLog.get_recent(10)
#=> [
# ["lib/user.ex", 1738350123456],
# ["lib/compile_log.ex", 1738350098765],
# ["lib/user.ex", 1738350045678]
# ]
# Get last 5
CompileLog.get_recent(5)
#=> [...]
# No compilations yet
CompileLog.get_recent(10)
#=> []
# Custom database
CompileLog.get_recent(20, :my_db)
#=> [...]
```
#### SQL Query
```sql
SELECT name, time
FROM auto_compile
ORDER BY time DESC
LIMIT ?
```
#### Performance
- Uses `idx_auto_compile_time` index
- O(log n) for ordering
- Very fast even with thousands of entries
---
### get_by_file/2
#### Description
Retrieves compilation history for a specific file, ordered by timestamp (newest first).
#### Spec
```elixir
@spec get_by_file(String.t(), keyword()) :: list()
```
#### Parameters
**file_path** :: `String.t()` (required)
- Path to the file
- Should match the path used in `log/2`
- Example: `"lib/user.ex"`
**opts** :: `keyword()` (default: `[]`)
- Options keyword list
#### Options
**:limit** :: `integer()` (default: `10`)
- Maximum logs to return
**:db** :: `atom()` (default: `:compile_log_db`)
- Database process name
#### Returns
**list()** :: List of log entries
- Each entry: `[id, name, time, git_diff, created_at, updated_at]`
- Ordered by `time DESC`
- Empty list if file has no logs or on error
#### Examples
```elixir
# Get last 5 compilations of user.ex
CompileLog.get_by_file("lib/user.ex", limit: 5)
#=> [
# [1738350123456, "lib/user.ex", 1738350123456, "diff...", 1738350123456, 1738350123456],
# [1738350098765, "lib/user.ex", 1738350098765, "", 1738350098765, 1738350098765],
# ...
# ]
# Default limit (10)
CompileLog.get_by_file("lib/my_module.ex")
#=> [...]
# With custom database
CompileLog.get_by_file("lib/test.ex", limit: 3, db: :my_db)
#=> [...]
# File never compiled
CompileLog.get_by_file("lib/new_file.ex")
#=> []
```
#### SQL Query
```sql
SELECT id, name, time, git_diff, created_at, updated_at
FROM auto_compile
WHERE name = ?
ORDER BY time DESC
LIMIT ?
```
#### Performance
- Uses `idx_auto_compile_name` index
- O(log n) for lookup
- Fast even with many compilations of same file
#### Use Cases
- View file edit history
- Review changes during debugging
- Track development focus
- Code review preparation
---
### get_git_diff/1
#### Description
Executes `git diff -U0 ` to capture the current git diff for a file with minimal context.
#### Spec
```elixir
@spec get_git_diff(String.t()) :: String.t() | nil
```
#### Parameters
**file_path** :: `String.t()` (required)
- Path to the file
- Relative to git repository root
- Example: `"lib/user.ex"`
#### Returns
**String.t()** :: Git diff output
- May be empty string if no changes
- Contains unified diff format
- Uses `-U0` flag (zero context lines)
**nil** :: Git command failed
- Not a git repository
- File not tracked by git
- System.cmd error
- Git not installed
#### Examples
```elixir
# File with uncommitted changes
CompileLog.get_git_diff("lib/user.ex")
#=> """
# diff --git a/lib/user.ex b/lib/user.ex
# index 1234567..abcdefg 100644
# --- a/lib/user.ex
# +++ b/lib/user.ex
# @@ -45 +45 @@ defmodule User do
# - @session_duration_seconds 86400
# + @session_duration_seconds 43200
# """
# File with no changes (clean)
CompileLog.get_git_diff("lib/user.ex")
#=> ""
# Not a git repository
CompileLog.get_git_diff("lib/test.ex")
#=> nil
# File doesn't exist
CompileLog.get_git_diff("lib/missing.ex")
#=> nil
```
#### Git Command
```bash
git diff -U0
```
**Flags:**
- `-U0` - Show zero lines of context around changes
- Minimal output for compact storage
- Only changed lines are shown
#### Error Handling
```elixir
def get_git_diff(file_path) do
case System.cmd("git", ["diff", "-U0", file_path], stderr_to_stdout: true) do
{output, 0} -> output
{_output, _exit_code} -> nil
end
rescue
_error -> nil
end
```
Gracefully handles:
- Non-git directories
- Missing files
- Git command errors
- System.cmd exceptions
#### Performance
- Typical execution: 5-10ms
- Depends on file size
- No database access
---
### cleanup_old_logs/2
#### Description
Deletes compilation logs older than a specified number of days.
#### Spec
```elixir
@spec cleanup_old_logs(non_neg_integer(), atom()) :: {:ok, non_neg_integer()} | {:error, any()}
```
#### Parameters
**days** :: `non_neg_integer()` (default: `30`)
- Delete logs older than this many days
- Based on `time` column
- Calculated from current system time
**db** :: `atom()` (default: `:compile_log_db`)
- Database process name
#### Returns
**{:ok, count}** :: Success
- `count` is number of deleted entries
- May be 0 if no old logs exist
**{:error, reason}** :: Failure
- Database delete failed
- Returns error reason
#### Examples
```elixir
# Delete logs older than 30 days (default)
CompileLog.cleanup_old_logs()
#=> {:ok, 15} # Deleted 15 entries
# Weekly cleanup
CompileLog.cleanup_old_logs(7)
#=> {:ok, 42}
# Daily cleanup
CompileLog.cleanup_old_logs(1)
#=> {:ok, 150}
# No old logs
CompileLog.cleanup_old_logs(30)
#=> {:ok, 0}
# Custom database
CompileLog.cleanup_old_logs(30, :my_db)
#=> {:ok, 5}
```
#### Time Calculation
```elixir
cutoff_time = System.system_time(:millisecond) - days * 24 * 60 * 60 * 1000
```
- Current time in milliseconds
- Subtract days converted to milliseconds
- All logs with `time < cutoff_time` are deleted
#### SQL Executed
```sql
DELETE FROM auto_compile WHERE time < ?
-- Then retrieves count:
SELECT changes()
```
#### Use Cases
- Regular database maintenance
- Manage storage space
- Comply with data retention policies
- Remove stale development logs
#### Best Practices
```elixir
# Monthly cleanup task
defmodule MyApp.Scheduler do
def monthly_cleanup do
CompileLog.cleanup_old_logs(30)
end
end
# Check size before/after
before_size = File.stat!("data/log.sqlite").size
{:ok, deleted} = CompileLog.cleanup_old_logs(30)
after_size = File.stat!("data/log.sqlite").size
saved = before_size - after_size
IO.puts("Deleted #{deleted} logs, saved #{saved} bytes")
```
---
### stats/1
#### Description
Aggregates compilation data to provide development statistics and insights.
#### Spec
```elixir
@spec stats(atom()) :: map()
```
#### Parameters
**db** :: `atom()` (default: `:compile_log_db`)
- Database process name
#### Returns
**map()** :: Statistics map with the following keys:
| Key | Type | Description |
|-----|------|-------------|
| `:total_compilations` | `integer()` | Total number of compilation logs |
| `:unique_files` | `integer()` | Number of distinct files compiled |
| `:most_compiled_file` | `{String.t(), integer()}` or `{nil, 0}` | File with most compilations and its count |
| `:first_compilation` | `integer()` or `nil` | Timestamp of earliest compilation |
| `:last_compilation` | `integer()` or `nil` | Timestamp of most recent compilation |
#### Examples
```elixir
# Normal case with data
CompileLog.stats()
#=> %{
# total_compilations: 150,
# unique_files: 23,
# most_compiled_file: {"lib/user.ex", 42},
# first_compilation: 1738265123456,
# last_compilation: 1738350123456
# }
# No compilations yet
CompileLog.stats()
#=> %{
# total_compilations: 0,
# unique_files: 0,
# most_compiled_file: {nil, 0},
# first_compilation: nil,
# last_compilation: nil
# }
# Analyze development patterns
stats = CompileLog.stats()
{file, count} = stats.most_compiled_file
avg = div(stats.total_compilations, max(stats.unique_files, 1))
IO.puts("""
Development Statistics:
Total compilations: #{stats.total_compilations}
Unique files: #{stats.unique_files}
Average per file: #{avg}
Hot spot: #{file} (#{count} times)
""")
```
#### SQL Queries
```sql
-- Total compilations
SELECT COUNT(*) FROM auto_compile
-- Unique files
SELECT COUNT(DISTINCT name) FROM auto_compile
-- Most compiled file
SELECT name, COUNT(*) as count
FROM auto_compile
GROUP BY name
ORDER BY count DESC
LIMIT 1
-- Time range
SELECT MIN(time), MAX(time) FROM auto_compile
```
#### Error Handling
Uses `with` pattern for graceful error handling:
```elixir
with {:ok, [[total]]} <- Sqler.sql(db, total_query),
{:ok, [[unique]]} <- Sqler.sql(db, unique_query),
# ...
do
%{total_compilations: total, ...}
else
_ -> %{total_compilations: 0, ...}
end
```
Returns zeroed stats if any query fails.
#### Use Cases
- Identify frequently modified files
- Track development activity patterns
- Find potential refactoring candidates
- Monitor project health
- Generate development reports
#### Performance
- Executes 4 SQL queries
- Uses aggregation functions
- Typically completes in 1-5ms
- Scales well with indexed columns
---
## Examples
### Basic Logging
```elixir
# Automatic logging (via AutoCompile)
# 1. Save lib/user.ex
# 2. AutoCompile compiles it
# 3. CompileLog.log("lib/user.ex") is called
# 4. Entry is stored with git diff
# Manual logging
CompileLog.log("lib/my_module.ex")
#=> {:ok, 1738350123456}
```
### Query Recent Activity
```elixir
# Get last 10 compilations
logs = CompileLog.get_recent(10)
# Format and display
Enum.each(logs, fn [name, time] ->
dt = DateTime.from_unix!(time, :millisecond)
formatted = Calendar.strftime(dt, "%H:%M:%S")
IO.puts("#{formatted} - #{name}")
end)
# Output:
# 14:23:45 - lib/user.ex
# 14:22:10 - lib/compile_log.ex
# 14:20:33 - lib/user.ex
```
### Track File History
```elixir
# Get compilation history
history = CompileLog.get_by_file("lib/user.ex", limit: 5)
IO.puts("Compilation history for lib/user.ex:")
Enum.each(history, fn [_id, _name, time, diff, _, _] ->
dt = DateTime.from_unix!(time, :millisecond)
formatted = Calendar.strftime(dt, "%Y-%m-%d %H:%M:%S")
diff_preview = String.slice(diff || "", 0..50)
IO.puts(" #{formatted}")
IO.puts(" #{diff_preview}...")
end)
```
### Development Analytics
```elixir
# Get statistics
stats = CompileLog.stats()
# Calculate averages
avg_per_file =
if stats.unique_files > 0 do
div(stats.total_compilations, stats.unique_files)
else
0
end
# Display report
IO.puts("""
=== Development Report ===
Activity:
Total compilations: #{stats.total_compilations}
Unique files: #{stats.unique_files}
Average per file: #{avg_per_file}
""")
# Show hot spot
case stats.most_compiled_file do
{file, count} when not is_nil(file) ->
percentage = round(count / stats.total_compilations * 100)
IO.puts("""
Hot Spot:
File: #{file}
Compilations: #{count}
Percentage: #{percentage}%
""")
if percentage > 30 do
IO.puts(" ⚠️ Consider refactoring - high edit frequency")
end
{nil, 0} ->
IO.puts("Hot Spot: None yet")
end
# Time range
if stats.first_compilation do
first = DateTime.from_unix!(stats.first_compilation, :millisecond)
last = DateTime.from_unix!(stats.last_compilation, :millisecond)
duration_ms = stats.last_compilation - stats.first_compilation
duration_hours = div(duration_ms, 1000 * 60 * 60)
IO.puts("""
Time Range:
First: #{Calendar.strftime(first, "%Y-%m-%d %H:%M:%S")}
Last: #{Calendar.strftime(last, "%Y-%m-%d %H:%M:%S")}
Duration: #{duration_hours} hours
""")
end
```
### Code Review Helper
```elixir
# Review all changes in last hour
hour_ago = System.system_time(:millisecond) - 3_600_000
recent = CompileLog.get_recent(50)
recent_hour =
Enum.filter(recent, fn [_name, time] ->
time > hour_ago
end)
IO.puts("Changes in last hour: #{length(recent_hour)} compilations\n")
# Group by file
by_file = Enum.group_by(recent_hour, fn [name, _] -> name end)
Enum.each(by_file, fn {file, compilations} ->
IO.puts("#{file} (#{length(compilations)} times)")
# Show latest diff
[[_, last_time]] = Enum.take(compilations, 1)
[[_, _, _, diff, _, _]] = CompileLog.get_by_file(file, limit: 1)
if diff && diff != "" do
IO.puts(diff)
else
IO.puts(" (no diff)")
end
IO.puts("")
end)
```
### Maintenance Tasks
```elixir
# Regular cleanup
defmodule CompileLogMaintenance do
def weekly_cleanup do
IO.puts("Running weekly compile log cleanup...")
before_size = File.stat!("data/log.sqlite").size
{:ok, deleted} = CompileLog.cleanup_old_logs(7)
after_size = File.stat!("data/log.sqlite").size
saved_kb = div(before_size - after_size, 1024)
IO.puts("""
Cleanup complete:
Deleted: #{deleted} logs
Space saved: #{saved_kb} KB
New size: #{div(after_size, 1024)} KB
""")
end
def monthly_report do
stats = CompileLog.stats()
# Generate report
report = """
=== Monthly Development Report ===
Activity: #{stats.total_compilations} compilations
Files: #{stats.unique_files} unique files
Top File: #{elem(stats.most_compiled_file, 0)} (#{elem(stats.most_compiled_file, 1)} times)
"""
# Save to file
filename = "reports/compile_log_#{Date.utc_today()}.txt"
File.write!(filename, report)
IO.puts("Report saved to #{filename}")
end
end
```
---
## Integration
### With AutoCompile
Automatic integration via `lib/auto_compile.ex`:
```elixir
defp handle_ex_change(path) do
case compile_file(path) do
{:ok, module} ->
# Log compilation automatically
CompileLog.log(path)
test_run(module)
{:error, error} ->
Logger.error("Compilation failed: #{inspect(error)}")
end
:ok
end
```
### Application Startup
Initialization in `lib/mcp/application.ex`:
```elixir
def start(_type, _args) do
children = [
{Sqler, name: "log", register: :compile_log_db},
{AutoCompile, ["lib/"]},
# ...
]
# ...
case Supervisor.start_link(children, opts) do
{:ok, pid} ->
CompileLog.setup_database(:compile_log_db)
{:ok, pid}
error ->
error
end
end
```
### Custom Integration
```elixir
# Manual compilation + logging
defmodule MyCompiler do
def compile_and_log(file_path) do
# Compile
result = Code.compile_file(file_path)
# Log
CompileLog.log(file_path)
result
end
end
```
---
## Performance
### Logging Overhead
| Operation | Time | Notes |
|-----------|------|-------|
| Git diff | ~5ms | Depends on file size |
| Database insert | ~1-2ms | SQLite write |
| Total overhead | ~5-10ms | Per compilation |
### Query Performance
| Query | Time | Notes |
|-------|------|-------|
| get_recent/2 | <1ms | Indexed by time |
| get_by_file/2 | <1ms | Indexed by name |
| stats/1 | 1-5ms | Multiple aggregations |
| cleanup_old_logs/2 | 5-20ms | Depends on number deleted |
### Storage
| Item | Size | Notes |
|------|------|-------|
| Empty diff entry | ~200 bytes | No git changes |
| Average entry | ~500 bytes - 1KB | Typical diff |
| Large diff entry | Up to several KB | Major changes |
| Database overhead | ~10% | SQLite metadata |
### Optimization Tips
```elixir
# Regular cleanup
CompileLog.cleanup_old_logs(30)
# Monitor database size
File.stat!("data/log.sqlite").size
# Use VACUUM to reclaim space (SQLite command)
Sqler.sql(:compile_log_db, "VACUUM")
```
---
## Troubleshooting
### No Logs Appearing
```elixir
# 1. Check database process
case Process.whereis(:compile_log_db) do
nil -> IO.puts("❌ Database not running")
pid -> IO.puts("✅ Database running: #{inspect(pid)}")
end
# 2. Check table exists
case Sqler.tables(:compile_log_db) do
tables when "auto_compile" in tables ->
IO.puts("✅ Table exists")
tables ->
IO.puts("❌ Table missing. Found: #{inspect(tables)}")
CompileLog.setup_database()
end
# 3. Test manual logging
case CompileLog.log("lib/test.ex") do
{:ok, id} -> IO.puts("✅ Logging works: #{id}")
{:error, reason} -> IO.puts("❌ Logging failed: #{reason}")
end
# 4. Check recent logs
case CompileLog.get_recent(5) do
[] -> IO.puts("❌ No logs found")
logs -> IO.puts("✅ Found #{length(logs)} logs")
end
```
### Git Diff Returns nil
```bash
# Not a git repository
cd /path/to/project
git status
# If not a git repo:
git init
# File not tracked
git add lib/your_file.ex
git status
# Test git diff manually
git diff -U0 lib/your_file.ex
```
```elixir
# Test from Elixir
CompileLog.get_git_diff("lib/user.ex")
|> case do
nil -> IO.puts("❌ Git diff failed")
"" -> IO.puts("✅ No changes (clean)")
diff -> IO.puts("✅ Diff:\n#{diff}")
end
```
### Database Errors
```elixir
# Check database file
case File.stat("data/log.sqlite") do
{:ok, stat} ->
IO.puts("✅ Database file exists: #{stat.size} bytes")
{:error, :enoent} ->
IO.puts("❌ Database file missing")
CompileLog.setup_database()
end
# Test database connection
case Sqler.sql(:compile_log_db, "SELECT 1") do
[[1]] -> IO.puts("✅ Database connection OK")
error -> IO.puts("❌ Database error: #{inspect(error)}")
end
# Re-initialize if needed
CompileLog.setup_database()
```
### Performance Issues
```elixir
# Check database size
size_kb = div(File.stat!("data/log.sqlite").size, 1024)
IO.puts("Database size: #{size_kb} KB")
if size_kb > 10_000 do
IO.puts("⚠️ Database is large (>10MB), consider cleanup")
CompileLog.cleanup_old_logs(30)
end
# Check log count
stats = CompileLog.stats()
IO.puts("Total logs: #{stats.total_compilations}")
if stats.total_compilations > 10_000 do
IO.puts("⚠️ Many logs, cleanup recommended")
end
```
---
**Documentation Generated:** 2026-01-31
**Module Version:** 1.0.0
**Elixir Version:** 1.19.5