Subject: [SCRIPT] Formicaio Log Cleanup Tool - Reclaim Disk Space Easily!
Me, but mostly AI made script to free up disk space by deleting old logs, from 32gb to 5gb on my setup. the scrip file can be downloaded with anttp: 1056ab9f3bfd8a9e896463669e3eac79ef447ae7d4e4f498971f023f27b7d286
or last in this post
I will shut up and let the AI speek…
Are your Formicaio installations (specifically node_data folders) eating up valuable disk space with old log files? I’ve developed a PowerShell script to help you easily manage and clean up these logs.
This script is designed to:
- Automatically Find Logs: It intelligently searches for your
node_data folders and their logs subdirectories, so you don’t have to manually locate them.
- Estimate Space Savings: Before deleting anything, it calculates and shows you exactly how much disk space you can reclaim by keeping logs for different hourly periods.
- Flexible Retention: You choose precisely how many hours of logs you want to keep.
- Safe Deletion: It is programmed to avoid deleting the currently active log file.
- Easy to Use: Interactive prompts guide you through the process, or you can run it via parameters for automation.
Prerequisites (Important!):
- Operating System: Windows (this is a PowerShell script).
- PowerShell Version: PowerShell 5.1 or newer.
- Administrator Privileges: You will need to run PowerShell as an Administrator for these two reasons:
- To set the PowerShell Execution Policy (a one-time setup).
- To ensure the script has the necessary permissions to find and delete files in potentially protected system directories.
- PowerShell Execution Policy: To allow the script to run, your PowerShell Execution Policy needs to be configured.
-
Open PowerShell as Administrator (right-click the PowerShell icon and select “Run as administrator”).
-
Run the following command: PowerShellSet-ExecutionPolicy RemoteSigned -Scope CurrentUser
-
Type Y and press Enter when prompted to confirm. (This setting allows local scripts to run while still providing security for downloaded scripts).
How to Use:
- Save the Script: Copy the entire code block provided below and save it as a
.ps1 file (e.g., CleanupFormicaioLogs.ps1) on your desktop or in a dedicated scripts folder.
- Run the Script:
-
Interactive Mode (Recommended for first use):
- Open PowerShell as Administrator.
- Navigate to the folder where you saved the script (e.g.,
cd C:\Users\YourUser\Desktop).
- Run the script by typing:
PowerShell.\CleanupFormicaioLogs.ps1
-
The script will then guide you through selecting a drive/path to search and choosing your desired log retention (in hours) based on the estimated savings.
-
Automated Mode (e.g., via Task Scheduler): You can pass parameters directly to bypass interactive prompts. For example, to search your C:\ drive and keep logs for the last 72 hours (3 days):PowerShell.\CleanupFormicaioLogs.ps1 -RootSearchPath "C:\" -HoursToKeep 72
-
(For Task Scheduler, the ‘Program/script’ field would be powershell.exe, and ‘Add arguments’ would be something like -ExecutionPolicy Bypass -File "C:\Path\To\Your\Script\CleanupFormicaioLogs.ps1" -RootSearchPath "C:\" -HoursToKeep 72)
Important Notes & Disclaimer:
- Always Test First! Before running the script with actual deletion in mind, I highly recommend running a test with a very high
HoursToKeep value (e.g., 9999) first. This will show you the estimated savings and which files would be deleted without actually removing anything.
- Backup: While this script is designed with safety in mind, it performs file deletions. If you have any critical log files or are unsure, always consider backing up your data before running any cleanup script.
- This script specifically targets
antnode.log* files within logs subfolders under node_data. If your Formicaio logs have different primary names or subfolder structures, you can adjust the -LogFileNamePattern and -LogSubfolderName parameters when running the script.
The Script Code:
<#
.SYNOPSIS
Cleans up old 'antnode' log files from 'node_data' directories.
.DESCRIPTION
This script automatically finds 'node_data' folders, typically created by 'antnode' applications.
It then scans 'logs' subfolders within each node, calculates potential disk space savings
for various retention periods (in hours), and finally deletes log files older than a user-specified number of hours.
.PARAMETER RootSearchPath
The top-level directory where the script should start searching for 'node_data' folders.
If provided, the script will attempt to use this path directly. If the path is invalid
or 'node_data' folders are not found there, it will fall back to an interactive menu.
.PARAMETER HoursToKeep
The number of hours to keep log files. Log files older than this will be deleted.
If not provided, the script will calculate and present estimated space savings
for various retention periods before prompting the user for this value.
.PARAMETER LogFileNamePattern
The filename pattern for log files to target (e.g., "antnode.log*").
Defaults to "antnode.log*".
.PARAMETER LogSubfolderName
The name of the subfolder within each node where logs are stored (e.g., "logs").
Defaults to "logs".
.EXAMPLE
# Interactive execution: Script will guide through drive selection and hours to keep
.\cleanup_antnode_logs.ps1
.EXAMPLE
# Automated execution: Search C:\ and keep logs for 24 hours
.\cleanup_antnode_logs.ps1 -RootSearchPath "C:\" -HoursToKeep 24
.EXAMPLE
# Automated execution with a specific, custom path and 7 days (168 hours) retention
.\cleanup_antnode_logs.ps1 -RootSearchPath "D:\MyApps\Formicaio" -HoursToKeep 168
.NOTES
Requires PowerShell 5.1 or newer.
Run with Administrator privileges if log files are in protected system directories.
It is recommended to run a test with -HoursToKeep 9999 (or any large number) to see the estimated savings
before running with actual deletion hours.
#>
param(
[string]$RootSearchPath = "", # Default to empty to trigger interactive menu
[int]$HoursToKeep = 0, # Default to 0 to trigger prompt
[string]$LogFileNamePattern = "antnode.log*",
[string]$LogSubfolderName = "logs"
)
# Helper function to format bytes into a readable size (KB, MB, GB)
function Format-Bytes {
param([long]$Bytes)
if ($Bytes -ge 1GB) {
"{0:N2} GB" -f ($Bytes / 1GB)
} elseif ($Bytes -ge 1MB) {
"{0:N2} MB" -f ($Bytes / 1MB)
} elseif ($Bytes -ge 1KB) {
"{0:N2} KB" -f ($Bytes / 1KB)
} else {
"$Bytes Bytes"
}
}
# --- Start: Robust Drive/Path Selection and node_data Search with Retry ---
$nodeDataFolders = $null # Will hold the array of found node_data paths
$finalSearchRoot = $null # The path that was successfully used to find node_data folders
# Flag to control the main loop for finding node_data folders
$searchSuccessful = $false
# Attempt to use RootSearchPath parameter if provided
if (-not [string]::IsNullOrWhiteSpace($RootSearchPath)) {
Write-Host "Attempting to use specified RootSearchPath parameter: '$RootSearchPath'..."
if (Test-Path $RootSearchPath -PathType Container) {
$nodeDataFolders = Get-ChildItem -Path $RootSearchPath -Directory -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq 'node_data' }
if ($nodeDataFolders) {
$searchSuccessful = $true
$finalSearchRoot = $RootSearchPath
} else {
Write-Warning "No 'node_data' folders found under '$RootSearchPath' (from parameter). Falling back to interactive selection."
}
} else {
Write-Warning "Specified RootSearchPath parameter '$RootSearchPath' does not exist or is not a directory. Falling back to interactive selection."
}
}
# Enter interactive selection loop if not successful with parameter
do {
if ($searchSuccessful) { break } # Exit loop if already found via parameter or previous interactive attempt
Write-Host "`n--- Select a search option for 'node_data' folders ---"
# 1. List available local drives
$drives = Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Name -match '^[A-Z]$' } | Sort-Object Name
$optionCounter = 1
if ($drives) {
Write-Host "Choose from available drives:"
foreach ($drive in $drives) {
Write-Host "$($optionCounter). $($drive.Name):\"
$optionCounter++
}
} else {
Write-Warning "No local drives detected. Please use the 'Enter custom path' option."
}
# 2. Option to enter a custom path manually
Write-Host "$($optionCounter). Enter a custom path manually (e.g., C:\YourAppFolder\)"
$optionCounter++
# 3. Option to exit the script
Write-Host "$($optionCounter). Exit script"
$userChoice = Read-Host "Enter the number corresponding to your choice"
$chosenOptionIndex = $null
$selectedSearchRoot = $null
if ([int]::TryParse($userChoice, [ref]$chosenOptionIndex)) {
if ($chosenOptionIndex -ge 1 -and $chosenOptionIndex -le $drives.Count) {
$selectedSearchRoot = "$($drives[$chosenOptionIndex - 1].Name):\"
} elseif ($chosenOptionIndex -eq ($drives.Count + 1)) { # Custom path option
$selectedSearchRoot = Read-Host "Please enter the full custom root directory (e.g., C:\MyFolder\)"
if ([string]::IsNullOrWhiteSpace($selectedSearchRoot)) {
Write-Warning "No custom path entered. Please try again."
continue # Restart the loop
}
} elseif ($chosenOptionIndex -eq ($drives.Count + 2)) { # Exit option
Write-Host "Exiting script as requested."
exit 0
} else {
Write-Warning "Invalid choice. Please enter a number from the provided list."
Start-Sleep -Seconds 1
continue # Restart the loop
}
} else { # Input was not a number
Write-Warning "Invalid input. Please enter a number."
Start-Sleep -Seconds 1
continue # Restart the loop
}
# Validate the selected/entered search root
if (-not (Test-Path $selectedSearchRoot -PathType Container)) {
Write-Warning "The specified path '$selectedSearchRoot' does not exist or is not a directory. Please verify."
Start-Sleep -Seconds 1
continue # Restart the loop
}
Write-Host "Searching for 'node_data' folders under '$selectedSearchRoot'..."
$nodeDataFolders = Get-ChildItem -Path $selectedSearchRoot -Directory -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq 'node_data' }
if ($nodeDataFolders) {
$searchSuccessful = $true
$finalSearchRoot = $selectedSearchRoot
} else {
Write-Warning "No 'node_data' folders found under '$selectedSearchRoot'. Please try a different drive/path."
Start-Sleep -Seconds 1
}
} while (-not $searchSuccessful) # Loop until node_data folders are successfully found
# Display found folders
Write-Host "Found the following 'node_data' folder(s):"
$nodeDataFolders | Select-Object FullName
Write-Host "" # Blank line for spacing
# --- End: Robust Drive/Path Selection and node_data Search with Retry ---
# --- Calculate and present potential free space (if HoursToKeep not provided as parameter) ---
if ($HoursToKeep -eq 0) {
Write-Host "`nCalculating potential disk space to be freed for different retention periods. This might take a moment..."
# Define sample hours to calculate potential freed space for
$sampleHoursOptions = @(1, 2, 5, 12, 24, 72, 168, 336, 720) # Added 2, 5, 12 hours here
# Hash table to store the total size (in bytes) that would be freed for each option
$freedSpaceEstimates = @{}
foreach ($hour in $sampleHoursOptions) {
$freedSpaceEstimates[$hour] = 0
}
$freedSpaceEstimates["0_hours_total"] = 0 # For total old logs if 0 hours retention
# Iterate through all found 'node_data' folders and their logs to gather sizes
foreach ($nodeDataFolder in $nodeDataFolders) {
$currentRootLogPath = $nodeDataFolder.FullName
$nodeFolders = Get-ChildItem -Path $currentRootLogPath -Directory
foreach ($nodeFolder in $nodeFolders) {
$logFilesPath = Join-Path -Path $nodeFolder.FullName -ChildPath $LogSubfolderName
if (Test-Path $logFilesPath -PathType Container) {
# Get all log files that are candidates for deletion (i.e., not the active 'antnode.log' without a timestamp)
$candidateLogFiles = Get-ChildItem -Path $logFilesPath -File -Filter $LogFileNamePattern | Where-Object { $_.Name -notlike "antnode.log" }
foreach ($file in $candidateLogFiles) {
$freedSpaceEstimates["0_hours_total"] += $file.Length
foreach ($hour in $sampleHoursOptions) {
# Change from AddDays to AddHours
$cutoffDate = (Get-Date).AddHours(-$hour)
if ($file.LastWriteTime -lt $cutoffDate) {
$freedSpaceEstimates[$hour] += $file.Length
}
}
}
}
}
}
Write-Host "`n--- Estimated Disk Space Savings ---"
Write-Host "If you keep logs for:"
foreach ($hour in $sampleHoursOptions | Sort-Object) { # Sort to display in ascending order
$displaySize = Format-Bytes -Bytes $freedSpaceEstimates[$hour]
Write-Host "$($hour) hour(s): $($displaySize) will be freed up."
}
$totalDisplaySize = Format-Bytes -Bytes $freedSpaceEstimates["0_hours_total"]
Write-Host "0 hour(s) (delete all old logs): $($totalDisplaySize) will be freed up."
Write-Host "------------------------------------`n"
# Prompt the user for the number of hours to keep logs based on the estimates
$hoursToKeepInput = Read-Host "Enter how many hours old log files should be kept based on the estimates above"
# Initialize $hoursToKeepRef to ensure it exists for [ref]
$hoursToKeepRef = $null
# Try to convert the input to an integer
if ([int]::TryParse($hoursToKeepInput, [ref]$hoursToKeepRef)) {
$HoursToKeep = $hoursToKeepRef # Set the parameter value
Write-Host "Keeping logs for the last $HoursToKeep hours."
} else {
# If the input is not a valid number, use a default value and notify the user.
$HoursToKeep = 168 # Default to 168 hours (7 days) if input is invalid
Write-Warning "Invalid input. Using default value of $HoursToKeep hours (7 days)."
}
} else {
Write-Host "Hours to keep specified as parameter: $HoursToKeep hours."
}
# --- Perform Log Cleanup ---
Write-Host "Starting log cleanup..."
# Iterate through each found 'node_data' folder
foreach ($nodeDataFolder in $nodeDataFolders) {
$currentRootLogPath = $nodeDataFolder.FullName
Write-Host "`n--- Processing logs in: $currentRootLogPath ---"
# Get all subfolders (the actual 'node' folders) inside this node_data folder
$nodeFolders = Get-ChildItem -Path $currentRootLogPath -Directory
foreach ($nodeFolder in $nodeFolders) {
$currentNodePath = $nodeFolder.FullName
$logFilesPath = Join-Path -Path $currentNodePath -ChildPath $LogSubfolderName
if (Test-Path $logFilesPath -PathType Container) {
# Find all log files that are candidates for deletion
$logFiles = Get-ChildItem -Path $logFilesPath -File -Filter $LogFileNamePattern | Where-Object { $_.Name -notlike "antnode.log" }
foreach ($file in $logFiles) {
# Change from AddDays to AddHours
$cutoffDate = (Get-Date).AddHours(-$HoursToKeep)
if ($file.LastWriteTime -lt $cutoffDate) {
Write-Host " DELETING: $($file.FullName) (Modified: $($file.LastWriteTime))"
Remove-Item -LiteralPath $file.FullName -Force
}
}
}
}
}
Write-Host "`nLog cleanup completed for all found 'node_data' folders."