Il y a 389 articles et 19 utilisateurs inscrits (ce bandeau est défini dans MediaWiki:Sitenotice)

Rotation des logs tomcat et Pleade

De AJLSM
Aller à la navigation Aller à la recherche

🧩 Archivage mensuel et nettoyage automatique des logs Tomcat / Java

🎯 Objectif

Ce document présente un script Bash permettant :

  • D’archiver automatiquement les fichiers journaliers et mensuels produits par des applications Java/Tomcat ;
  • De créer une archive compressée .gz unique par mois et par type de log ;
  • De supprimer les fichiers quotidiens une fois archivés correctement ;
  • De purger les archives plus anciennes qu’une période définie ;
  • De gérer plusieurs formats de nommage de fichiers :
    • manager.2025-11-07.log (format point)
    • sdx-20251107.log (format tiret)
    • pleade-stats-202511.log (format tiret mensuel).

Paramètres à modifier selon l’installation

#---------- PARAMÈTRES À ADAPTER ----------
DIRS=(
  "/home/ajlsm/tomcats/preprod/logs"
  "/home/ajlsm/tomcats/slecture/logs"
  "/home/ajlsm/tomcats/internet-consultation/logs"
  "/home/ajlsm/tomcats/internet-consultation/webapps/pleade/WEB-INF/logs"
  "/home/ajlsm/tomcats/slecture/webapps/pleade/WEB-INF/logs"
  "/home/ajlsm/tomcats/preprod/webapps/pleade/WEB-INF/logs"
)

RETENTION_MONTHS=6
BATCH_SIZE=0                 # 0 = désactivé ; 20 = regrouper dès qu'il y a 20 fichiers
DRY_RUN=0                    # 1 = ne rien écrire/supprimer, juste logguer
NORMALIZE_SUFFIX=1           # 1 = corriger *.txt-YYYYMMDD / *.log-YYYYMMDD avant archivage
ARCHIVE_CURRENT_MONTH=0      # 1 = inclure le mois courant (ré-agrège au fil du mois)

# Familles par style + extensions
declare -A FAMILIES_DOT
FAMILIES_DOT["manager"]="log"
FAMILIES_DOT["localhost_access_log"]="txt"
FAMILIES_DOT["catalina"]="log"
FAMILIES_DOT["localhost"]="log"
FAMILIES_DOT["host-manager"]="log"

declare -A FAMILIES_HYPH
FAMILIES_HYPH["auto-publication"]="log"
FAMILIES_HYPH["cocoon"]="log"
FAMILIES_HYPH["oai"]="log"
FAMILIES_HYPH["publication"]="log"
FAMILIES_HYPH["sdx"]="log"

declare -A FAMILIES_HMTH
FAMILIES_HMTH["pleade-stats"]="log"

⚙️ 1. Script d’archivage (archive-logs.sh)

Créer le fichier :
/usr/local/sbin/archive-logs.sh
et lui donner les droits d’exécution (chmod +x /usr/local/sbin/archive-logs.sh).

#!/usr/bin/env bash
# archive-logs.sh — regroupe les logs existants en archives mensuelles uniques (.gz)
# - Supporte trois styles de naming :
#   1) DOT  : base.YYYY-MM-DD.ext           (ex: manager.2025-11-07.log)
#   2) HYPH : base-YYYYMMDD.ext             (ex: sdx-20251107.log)
#   3) HMTH : base-YYYYMM.ext               (ex: pleade-stats-202511.log)
# - Ne modifie pas les fichiers lors de la lecture ; supprime uniquement si l’archive est OK
# - Purge les archives plus vieilles que RETENTION_MONTHS
# - (Optionnel) Normalise *.txt-YYYYMMDD / *.log-YYYYMMDD avant archivage

set -euo pipefail
IFS=$'\n\t'
umask 022

# ---------- PARAMÈTRES À ADAPTER ----------
DIRS=(
  "/home/ajlsm/tomcats/preprod/logs"
  "/home/ajlsm/tomcats/slecture/logs"
  "/home/ajlsm/tomcats/internet-consultation/logs"
  "/home/ajlsm/tomcats/internet-consultation/webapps/pleade/WEB-INF/logs"
  "/home/ajlsm/tomcats/slecture/webapps/pleade/WEB-INF/logs"
  "/home/ajlsm/tomcats/preprod/webapps/pleade/WEB-INF/logs"
)

RETENTION_MONTHS=6
BATCH_SIZE=0                 # 0 = désactivé ; 20 = regrouper dès qu'il y a 20 fichiers
DRY_RUN=0                    # 1 = ne rien écrire/supprimer, juste logguer
NORMALIZE_SUFFIX=1           # 1 = corriger *.txt-YYYYMMDD / *.log-YYYYMMDD avant archivage
ARCHIVE_CURRENT_MONTH=0      # 1 = inclure le mois courant (ré-agrège au fil du mois)

# Familles par style + extensions
declare -A FAMILIES_DOT
FAMILIES_DOT["manager"]="log"
FAMILIES_DOT["localhost_access_log"]="txt"
FAMILIES_DOT["catalina"]="log"
FAMILIES_DOT["localhost"]="log"
FAMILIES_DOT["host-manager"]="log"

declare -A FAMILIES_HYPH
FAMILIES_HYPH["auto-publication"]="log"
FAMILIES_HYPH["cocoon"]="log"
FAMILIES_HYPH["oai"]="log"
FAMILIES_HYPH["publication"]="log"
FAMILIES_HYPH["sdx"]="log"

declare -A FAMILIES_HMTH
FAMILIES_HMTH["pleade-stats"]="log"
# -------------------------------------------

log() { printf "[%s] %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; }

current_month() { date +%Y-%m; }

month_from_dot_name() { sed -nE 's/.*([0-9]{4})-([0-9]{2})-[0-9]{2}\.[^.]+$/\1-\2/p'; }
month_from_hyph_name() { awk 'match($0, /-[0-9]{8}\.[^.]+$/){y=substr($0,RSTART+1,4);m=substr($0,RSTART+5,2);print y"-"m}'; }
month_from_hmth_name() { awk 'match($0, /-[0-9]{6}\.[^.]+$/){y=substr($0,RSTART+1,4);m=substr($0,RSTART+5,2);print y"-"m}'; }

do_mv()     { (( DRY_RUN )) && log "[DRY] mv '$1' '$2'" || mv -- "$1" "$2"; }
do_rm()     { (( DRY_RUN )) && log "[DRY] rm -f $*" || rm -f -- "$@"; }
do_gzip_f() { (( DRY_RUN )) && log "[DRY] gzip -f '$1'" || gzip -f "$1"; }

normalize_suffixes() {
  for d in "${DIRS[@]}"; do
    [[ -d "$d" ]] || continue
    shopt -s nullglob
    for f in "$d"/*.txt-????????; do
      [[ "$f" =~ ^(.*\.txt)-[0-9]{8}$ ]] || continue
      new="${BASH_REMATCH[1]}"
      [[ -e "$new" ]] && { log "Skip (existe déjà) : $new"; continue; }
      do_mv "$f" "$new" && log "$f → $new"
    done
    for f in "$d"/*.log-????????; do
      [[ "$f" =~ ^(.*\.log)-[0-9]{8}$ ]] || continue
      new="${BASH_REMATCH[1]}"
      [[ -e "$new" ]] && { log "Skip (existe déjà) : $new"; continue; }
      do_mv "$f" "$new" && log "$f → $new"
    done
    shopt -u nullglob
  done
}

build_outname() {
  local style="$1" dir="$2" base="$3" month="$4" ext="$5"
  if [[ "$style" == "hyph" ]]; then
    printf "%s/%s-%s.%s.gz" "$dir" "$base" "$month" "$ext"
  else
    printf "%s/%s.%s.%s.gz" "$dir" "$base" "$month" "$ext"
  fi
}

make_monthly_archive() {
  local style="$1"; shift
  local dir="$1"; shift
  local base="$1"; shift
  local ext="$1"; shift
  local -a list=( "$@" )
  [[ ${#list[@]} -eq 0 ]] && return 0

  local month out tmp
  case "$style" in
    dot)  month="$(basename -- "${list[0]}" | month_from_dot_name)";;
    hyph) month="$(basename -- "${list[0]}" | month_from_hyph_name)";;
    hmth) month="$(basename -- "${list[0]}" | month_from_hmth_name)";;
  esac
  [[ -z "$month" ]] && { log "⚠️ Impossible d’extraire le mois pour ${list[0]}"; return 0; }

  out="$(build_outname "$style" "$dir" "$base" "$month" "$ext")"
  tmp="${out}.tmp"

  if [[ -s "$out" ]]; then
    log "Archive déjà présente: $out"
    return 0
  fi

  log "Création archive mensuelle: $out"
  : > "$tmp"
  for f in "${list[@]}"; do
    (( DRY_RUN )) && log "[DRY] cat '$f' >> '$tmp'" || cat -- "$f" >> "$tmp"
  done
  (( DRY_RUN )) && log "[DRY] gzip -f '$tmp' && mv '${tmp}.gz' '$out'" || { do_gzip_f "$tmp"; do_mv "${tmp}.gz" "$out"; }

  if [[ -s "$out" || ( $DRY_RUN -eq 1 ) ]]; then
    log "Archive OK ⇒ suppression des fichiers (${#list[@]})"
    do_rm "${list[@]}"
  else
    log "⚠️ Échec création archive: $out (aucune suppression faite)"
  fi
}

process_style() {
  local style="$1"; shift
  declare -n families="$1"
  local nowm; nowm="$(current_month)"

  for d in "${DIRS[@]}"; do
    [[ -d "$d" ]] || continue
    for base in "${!families[@]}"; do
      read -r -a exts <<< "${families[$base]}"
      for ext in "${exts[@]}"; do
        shopt -s nullglob
        local -a files=()
        case "$style" in
          dot)  files=( "$d"/${base}.[0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9].${ext} );;
          hyph) files=( "$d"/${base}-[0-9][0-9][0-9][0-9][0-1][0-9][0-3][0-9].${ext} );;
          hmth) files=( "$d"/${base}-[0-9][0-9][0-9][0-9][0-1][0-9].${ext} );;
        esac
        shopt -u nullglob
        [[ ${#files[@]} -eq 0 ]] && continue

        declare -A months=()
        local f m
        for f in "${files[@]}"; do
          case "$style" in
            dot)  m="$(basename -- "$f" | month_from_dot_name)";;
            hyph) m="$(basename -- "$f" | month_from_hyph_name)";;
            hmth) m="$(basename -- "$f" | month_from_hmth_name)";;
          esac
          [[ -z "$m" ]] && continue
          if (( ! ARCHIVE_CURRENT_MONTH )) && [[ "$m" == "$nowm" ]]; then
            continue
          fi
          months["$m"]=1
        done
        [[ ${#months[@]} -eq 0 ]] && continue

        for m in "${!months[@]}"; do
          local -a month_files=()
          y="${m%%-*}"; mm="${m##*-}"
          while IFS= read -r line; do month_files+=( "$line" ); done < <(
            printf "%s\n" "${files[@]}" |
            grep -E "/${base}[-\.](${y}${mm}|${m})" |
            LC_ALL=C sort
          )
          [[ ${#month_files[@]} -eq 0 ]] && continue
          make_monthly_archive "$style" "$d" "$base" "$ext" "${month_files[@]}"
        done
      done
    done
  done
}

purge_old_archives() {
  local days=$(( RETENTION_MONTHS * 31 ))
  for d in "${DIRS[@]}"; do
    [[ -d "$d" ]] || continue
    find "$d" -maxdepth 1 -type f \
      \( -regex '.*/[^/]+\.[0-9]{4}-[0-9]{2}\.(log|txt)\.gz' \
         -o -regex '.*/[^/]+-[0-9]{4}-[0-9]{2}\.(log|txt)\.gz' \) \
      -mtime +$days -print -delete
  done
}

LOCK="/var/lock/archive-logs.lock"
mkdir -p /var/lock
exec 9>"$LOCK" || exit 1
flock -n 9 || { echo "Déjà en cours, abandon."; exit 0; }
trap 'flock -u 9' EXIT

log "=== Début archivage logs ==="
if (( NORMALIZE_SUFFIX )); then
  log "Normalisation des suffixes *.txt-YYYYMMDD / *.log-YYYYMMDD"
  normalize_suffixes
fi
process_style dot  FAMILIES_DOT
process_style hyph FAMILIES_HYPH
process_style hmth FAMILIES_HMTH
purge_old_archives
log "=== Fin archivage logs ==="

🕐 2. Cron automatique

➤ Exécution mensuelle (chaque 1er du mois à 06:10)

10 6 1 * * /usr/local/sbin/archive-logs.sh >> /var/log/archive-logs.log 2>&1

➤ Option quotidienne (si BATCH_SIZE > 0))

5 6 * * * /usr/local/sbin/archive-logs.sh >> /var/log/archive-logs.log 2>&1

🧹 3. Renommage automatique

Si certains fichiers sont mal nommés (suffixes dupliqués comme .txt-YYYYMMDD ou .log-YYYYMMDD), le script les corrige automatiquement grâce à NORMALIZE_SUFFIX=1.
Tu peux aussi lancer cette normalisation seule :

bash /usr/local/sbin/archive-logs.sh

✅ 4. Résumé des familles gérées

Style Familles Exemple fichier Archive produite
DOT manager, localhost_access_log, catalina, localhost, host-manager manager.2025-11-07.log manager.2025-11.log.gz
HYPH auto-publication, cocoon, oai, publication, sdx sdx-20251107.log sdx-2025-11.log.gz
HMTH pleade-stats pleade-stats-202511.log pleade-stats-2025-11.log.gz

🔒 5. Sécurité & cohérence

  • Verrou via /var/lock/archive-logs.lock pour éviter les exécutions simultanées.
  • Test à blanc possible avec DRY_RUN=1.
  • Purge automatique après RETENTION_MONTHS mois.
  • Aucune altération des fichiers pendant la lecture : suppression uniquement après création réussie de l’archive.



🧭 Exemple avant/après

Avant

sdx-20251101.log
sdx-20251102.log
sdx-20251103.log
...
pleade-stats-202510.log

Après exécution

sdx-2025-11.log.gz
pleade-stats-2025-10.log.gz

📦 Résumé technique

Élément Valeur
Fichier script /usr/local/sbin/archive-logs.sh
Logs d’exécution /var/log/archive-logs.log
Cron mensuel 10 6 1 * *
Rétention 6 mois (RETENTION_MONTHS=6)
Mode test DRY_RUN=1
Verrouillage /var/lock/archive-logs.lock