# Compilation Log System Documentation ## Overview Automatic logging system for file compilations with git diff tracking. Integrates with AutoCompile to record every file change, compilation time, and the exact git diff at the moment of compilation. ## Features - ✅ **Automatic logging** - Logs triggered on every .ex file compilation - ✅ **Git diff capture** - Stores `git diff -U0` output for each compilation - ✅ **Timestamp tracking** - Millisecond precision timestamps - ✅ **File history** - Query compilation history per file - ✅ **Statistics** - Track most compiled files, compilation frequency - ✅ **Cleanup utilities** - Remove old logs automatically - ✅ **SQLite storage** - Persistent logs in data/log.sqlite ## Database Schema ```sql CREATE TABLE auto_compile ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, -- File path (e.g., "lib/user.ex") time INTEGER NOT NULL, -- Unix timestamp in milliseconds git_diff TEXT, -- Output of `git diff -U0` created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL ); CREATE INDEX idx_auto_compile_name ON auto_compile(name); CREATE INDEX idx_auto_compile_time ON auto_compile(time); ``` ## Setup ### Installation The compile log system is automatically configured in `lib/mcp/application.ex`: ```elixir children = [ # ... {Sqler, name: "data/log.sqlite", register: :compile_log_db}, {AutoCompile, ["lib/"]}, # ... ] # In start/2 CompileLog.setup_database(:compile_log_db) ``` ### Integration with AutoCompile When AutoCompile detects a file change and successfully compiles it: ```elixir # In lib/auto_compile.ex defp handle_ex_change(path) do case compile_file(path) do {:ok, module} -> CompileLog.log(path) # Log the compilation test_run(module) # ... end end ``` ## Usage ### View Recent Compilations ```elixir # Get last 10 compilations CompileLog.get_recent(10) # Returns list of [id, name, time, git_diff, created_at, updated_at] [ [1738350123456, "lib/user.ex", 1738350123456, "diff --git...", 1738350123456, 1738350123456], [1738350098765, "lib/compile_log.ex", 1738350098765, "diff --git...", 1738350098765, 1738350098765], ... ] ``` ### View File History ```elixir # Get compilation history for specific file CompileLog.get_by_file("lib/user.ex", limit: 5) # With custom database CompileLog.get_by_file("lib/my_module.ex", limit: 10, db: :compile_log_db) ``` ### Get Git Diff ```elixir # Get current git diff for a file CompileLog.get_git_diff("lib/user.ex") # Returns: """ 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 """ ``` ### Manual Logging ```elixir # Manually log a compilation CompileLog.log("lib/my_module.ex") {:ok, 1738350123456} ``` ### Statistics ```elixir CompileLog.stats() # Returns: %{ total_compilations: 150, unique_files: 23, most_compiled_file: {"lib/user.ex", 42}, first_compilation: 1738265123456, last_compilation: 1738350123456 } ``` ### Cleanup Old Logs ```elixir # Delete logs older than 30 days CompileLog.cleanup_old_logs(30) {:ok, 15} # Deleted 15 entries # Delete logs older than 7 days CompileLog.cleanup_old_logs(7) ``` ## Examples ### Run Demo ```elixir # In IEx iex> CompileLogExamples.demo() === CompileLog Demo === 1. Recent Compilation Logs (last 10)... Found 5 recent compilations: • lib/compile_log.ex Time: 2026-01-31 11:50:23 Diff: diff --git a/lib/compile_log.ex b/lib/compile_log.ex • lib/user.ex Time: 2026-01-31 11:48:15 Diff: (no changes) ... ``` ### Show Recent Diff ```elixir iex> CompileLogExamples.show_recent_diff() === Recent Compilation === File: lib/user.ex Time: 2026-01-31 11:48:15 Git Diff: ---------------------------------------- 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 ---------------------------------------- ``` ### Show File-Specific Diff ```elixir iex> CompileLogExamples.show_file_diff("lib/user.ex") === Compilation Log for lib/user.ex === Time: 2026-01-31 11:48:15 Git Diff: ---------------------------------------- diff --git a/lib/user.ex b/lib/user.ex ... ---------------------------------------- ``` ### Test Git Diff ```elixir iex> CompileLogExamples.test_git_diff("lib/user.ex") === Testing git diff for lib/user.ex === Git diff output: ---------------------------------------- diff --git a/lib/user.ex b/lib/user.ex index 1234567..abcdefg 100644 ... ---------------------------------------- ``` ### Manual Log Test ```elixir iex> CompileLogExamples.test_manual_log("lib/my_module.ex") === Manual Compilation Log Test === ✓ Successfully logged compilation File: lib/my_module.ex Log ID: 1738350123456 Retrieving log entry... ✓ Log entry found: Name: lib/my_module.ex Time: 2026-01-31 11:50:23 Diff: diff --git a/lib/my_module.ex... === Test Complete === ``` ## API Reference ### CompileLog.setup_database/1 Initialize database schema. ```elixir CompileLog.setup_database(:compile_log_db) :ok ``` ### CompileLog.log/2 Log a compilation event. ```elixir @spec log(String.t(), atom()) :: {:ok, integer()} | {:error, any()} CompileLog.log("lib/user.ex") {:ok, 1738350123456} CompileLog.log("lib/user.ex", :my_custom_db) {:ok, 1738350123456} ``` ### CompileLog.get_recent/2 Get recent compilation logs. ```elixir @spec get_recent(non_neg_integer(), atom()) :: list() CompileLog.get_recent(10) CompileLog.get_recent(20, :compile_log_db) ``` ### CompileLog.get_by_file/2 Get logs for specific file. ```elixir @spec get_by_file(String.t(), keyword()) :: list() CompileLog.get_by_file("lib/user.ex", limit: 5) CompileLog.get_by_file("lib/user.ex", limit: 10, db: :my_db) ``` ### CompileLog.get_git_diff/1 Get git diff for file. ```elixir @spec get_git_diff(String.t()) :: String.t() | nil CompileLog.get_git_diff("lib/user.ex") # Returns diff string or nil ``` ### CompileLog.cleanup_old_logs/2 Delete old log entries. ```elixir @spec cleanup_old_logs(non_neg_integer(), atom()) :: {:ok, non_neg_integer()} | {:error, any()} CompileLog.cleanup_old_logs(30) {:ok, 15} # Deleted 15 entries ``` ### CompileLog.stats/1 Get compilation statistics. ```elixir @spec stats(atom()) :: map() CompileLog.stats() %{ total_compilations: 150, unique_files: 23, most_compiled_file: {"lib/user.ex", 42}, first_compilation: 1738265123456, last_compilation: 1738350123456 } ``` ## Use Cases ### Development Workflow Tracking Track which files are being modified most during development: ```elixir stats = CompileLog.stats() {most_file, compile_count} = stats.most_compiled_file IO.puts("You've compiled #{most_file} #{compile_count} times today!") ``` ### Code Review History View the exact changes at each compilation: ```elixir logs = CompileLog.get_by_file("lib/user.ex", limit: 10) Enum.each(logs, fn [_id, name, time, diff, _, _] -> IO.puts("\n=== #{name} at #{format_time(time)} ===") IO.puts(diff) end) ``` ### Debugging See what changed when a bug was introduced: ```elixir # Get compilations from last hour recent = CompileLog.get_recent(50) suspicious_logs = Enum.filter(recent, fn [_, _, time, _, _, _] -> time > (System.system_time(:millisecond) - 3_600_000) end) # Review each diff Enum.each(suspicious_logs, fn [_, name, _, diff, _, _] -> IO.puts("#{name}:") IO.puts(diff) end) ``` ### Audit Trail Maintain a complete history of code changes during development: ```elixir # Never delete logs, just query as needed all_changes = CompileLog.get_by_file("lib/critical_module.ex", limit: 1000) # Export to file for documentation File.write!("changes_history.txt", format_history(all_changes)) ``` ## Database Location ``` data/log.sqlite - Compilation log database (auto-created) ``` ## Performance - **Logging overhead**: ~5-10ms per compilation (git diff + database insert) - **Query performance**: Indexed by file name and time - **Storage**: ~1KB per log entry (depends on diff size) - **Cleanup**: Run periodic cleanup to prevent database growth ## Configuration ### Adjust Logged Directories In `lib/mcp/application.ex`: ```elixir {AutoCompile, ["lib/", "priv/", "config/"]} ``` ### Custom Database Name ```elixir # In application.ex {Sqler, name: "custom_log", register: :compile_log_db} # This creates: data/custom_log.sqlite # With custom directory {Sqler, name: "custom_log", data_dir: "/var/logs", register: :compile_log_db} # This creates: /var/logs/custom_log.sqlite # In code CompileLog.log("lib/user.ex", :compile_log_db) ``` ### Git Diff Options Modify `CompileLog.get_git_diff/1` to customize diff output: ```elixir # Show more context (3 lines instead of 0) System.cmd("git", ["diff", "-U3", file_path]) # Show function names System.cmd("git", ["diff", "-U0", "-p", file_path]) # Word diff System.cmd("git", ["diff", "--word-diff", file_path]) ``` ## Best Practices 1. **Regular cleanup** - Run `cleanup_old_logs/1` weekly or monthly 2. **Monitor storage** - Check `data/log.sqlite` size periodically 3. **Backup logs** - Include in database backup strategy 4. **Review stats** - Use stats to identify hot spots in development ## Troubleshooting ### No logs appearing ```elixir # Check if database is running Process.whereis(:compile_log_db) # Check if table exists Sqler.raw_query(:compile_log_db, "SELECT COUNT(*) FROM auto_compile") # Manually test logging CompileLog.log("lib/test.ex") ``` ### Git diff returns nil ```elixir # Not a git repository # Run: git init # File not tracked by git # Run: git add lib/your_file.ex # Test git command System.cmd("git", ["diff", "-U0", "lib/your_file.ex"]) ``` ### Database locked errors ```elixir # Check for concurrent access # SQLite handles this automatically, but ensure you're not # accessing from multiple processes simultaneously ``` ## Integration Examples ### With CI/CD Export logs before deployment: ```elixir # In deployment script logs = CompileLog.get_recent(100) File.write!("deployment_changes.json", Jason.encode!(logs)) ``` ### With Notifications Alert on high compilation frequency: ```elixir # In AutoCompile defp handle_ex_change(path) do case compile_file(path) do {:ok, module} -> CompileLog.log(path) # Check if compiled too frequently recent_count = length(CompileLog.get_by_file(path, limit: 10)) if recent_count > 5 do Logger.warning("#{path} compiled #{recent_count} times recently") end test_run(module) end end ``` ## Future Enhancements Potential additions: - [ ] Compilation duration tracking - [ ] Error/warning count logging - [ ] File size delta tracking - [ ] Module dependency tracking - [ ] Test result logging - [ ] Web UI for browsing logs - [ ] Export to JSON/CSV - [ ] Integration with version control hooks ## Summary The CompileLog system provides automatic, transparent logging of all file compilations with git diff tracking. It's designed to be: - **Zero-configuration** - Works automatically once installed - **Low-overhead** - Minimal performance impact - **Useful** - Provides insights into development workflow - **Persistent** - SQLite-backed permanent storage - **Queryable** - Rich API for retrieving and analyzing logs Use `CompileLogExamples.demo()` to explore all features interactively.