#!/usr/bin/env bash ######################################################################################### #### squid-exporter.sh — Prometheus metrics exporter for Squid proxy #### #### Exports cache performance, hit rates, request counts, client/server stats, #### #### memory usage, and file descriptor utilization as Prometheus metrics #### #### Requires: bash 4+, squidclient or curl, jq optional #### #### #### #### Author: Phil Connor #### #### Contact: contact@mylinux.work #### #### License: MIT #### #### Version 1.00 #### #### #### #### Usage: #### #### ./squid-exporter.sh --http --port 9619 #### #### #### #### See --help for all options. #### ######################################################################################### set -uo pipefail # ============================================================================ # CONFIGURATION VARIABLES # ============================================================================ SQUID_HOST="${SQUID_HOST:-localhost}" SQUID_PORT="${SQUID_PORT:-3128}" SQUID_MGR_PORT="${SQUID_MGR_PORT:-3128}" TEXTFILE_DIR="/var/lib/node_exporter" OUTPUT_FILE="" HTTP_MODE=false HTTP_PORT=9619 EXPORTER_VERSION="1.00" # ============================================================================ # HELPER FUNCTIONS # ============================================================================ show_usage() { cat <&2; exit 1 ;; esac done } # ============================================================================ # SQUID DATA COLLECTION # ============================================================================ # Fetch a squid manager page # Args: $1 - manager page (e.g., "info", "counters", "5min") # Returns: raw text output on stdout fetch_mgr_page() { local page="$1" if command -v squidclient >/dev/null 2>&1; then squidclient -h "$SQUID_HOST" -p "$SQUID_PORT" "mgr:${page}" 2>/dev/null elif command -v curl >/dev/null 2>&1; then curl -sf --max-time 10 "http://${SQUID_HOST}:${SQUID_MGR_PORT}/squid-internal-mgr/${page}" 2>/dev/null else echo "ERROR: Neither squidclient nor curl found" >&2 return 1 fi } # Check if Squid is responding # Returns: 0 if OK, 1 if error check_squid() { local output output=$(fetch_mgr_page "info" 2>/dev/null) if [[ -z "$output" ]]; then return 1 fi return 0 } # Extract a numeric value from squid output # Args: $1 - text data, $2 - field pattern (grep), $3 - awk field number (default 2) # Returns: numeric value or 0 extract_value() { local data="$1" local pattern="$2" local field="${3:-0}" local result if [[ "$field" == "0" ]]; then # Take the last numeric token on the matched line result=$(echo "$data" | grep -i "$pattern" | head -1 | grep -oE '[0-9]+\.?[0-9]*' | tail -1) else result=$(echo "$data" | grep -i "$pattern" | head -1 | awk "{print \$$field}") fi # Strip any non-numeric characters except dot result=$(echo "$result" | tr -cd '0-9.') echo "${result:-0}" } # Extract a specific counter from mgr:counters output # Format: "counter_name = value" # Args: $1 - counters data, $2 - counter name # Returns: numeric value or 0 extract_counter() { local data="$1" local name="$2" local result result=$(echo "$data" | grep -E "^${name} = " | head -1 | awk -F' = ' '{print $2}') result=$(echo "$result" | tr -cd '0-9.') echo "${result:-0}" } # ============================================================================ # METRIC GENERATION # ============================================================================ generate_metrics() { local script_start script_start=$(date +%s%N 2>/dev/null || date +%s) if ! check_squid; then cat < 1) printf \"%.6f\", v/1000; else printf \"%.6f\", v}") dns_svc_time=$(awk "BEGIN {v=$dns_svc_time; if (v > 1) printf \"%.6f\", v/1000; else printf \"%.6f\", v}") icp_svc_time=$(awk "BEGIN {v=$icp_svc_time; if (v > 1) printf \"%.6f\", v/1000; else printf \"%.6f\", v}") cat </dev/null || date +%s) script_duration=$(awk "BEGIN {printf \"%.3f\", ($end_ns - $script_start) / 1000000000}") else script_duration=$(( $(date +%s) - script_start )) fi cat <&2 if ! command -v nc >/dev/null 2>&1; then echo "ERROR: netcat (nc) required for HTTP mode" >&2 exit 1 fi while true; do { read -r request if [[ "$request" =~ ^GET\ /metrics ]]; then echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/plain; version=0.0.4\r\n\r" generate_metrics else echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r" cat < Squid Exporter v${EXPORTER_VERSION}

Squid Prometheus Exporter v${EXPORTER_VERSION}

Metrics

Exports Squid proxy cache performance and operational metrics.

EOF fi } | nc -l -p "$HTTP_PORT" -q 1 2>/dev/null done } # ============================================================================ # MAIN EXECUTION # ============================================================================ main() { parse_args "$@" if [[ "$HTTP_MODE" == true ]]; then run_http_server elif [[ -n "$OUTPUT_FILE" ]]; then local output_dir output_dir="$(dirname "$OUTPUT_FILE")" mkdir -p "$output_dir" local temp_file temp_file=$(mktemp "${output_dir}/.squid_metrics.XXXXXX") if ! generate_metrics > "$temp_file" 2>/dev/null; then rm -f "$temp_file" echo "ERROR: Failed to generate metrics" >&2 exit 1 fi local file_lines file_lines=$(wc -l < "$temp_file" 2>/dev/null || echo 0) if [[ "$file_lines" -lt 10 ]]; then rm -f "$temp_file" echo "ERROR: Metrics file too small ($file_lines lines), keeping previous" >&2 exit 1 fi chmod 644 "$temp_file" mv -f "$temp_file" "$OUTPUT_FILE" echo "Metrics written to $OUTPUT_FILE ($file_lines lines)" >&2 else generate_metrics fi } main "$@"