Rotar logs en Windows
Uno de los incordios de usar Apache en Windows es que no trae una forma decente de rotar los logs. La utilidad
incluida, rotatelogs, tiene
entre otros vicios la desagradable tendencia de ir dejando procesos colgados en memoria. La alternativa que
recomiendan en el manual, cronolog, cojea del
mismo pie. Hay módulos que no tienen ese fallo (aunque sí otros) pero nunca hay binarios para tu
versión de Apache. Un lío, vamos.
Este script *.bat permite rotar los logs de Apache en Windows (instalado como servicio) usando un
mecanismo parecido al que emplea el programa de Linux logrotate
(no confundir con ninguno de los anteriores), a saber:
- Detener Apache
- Mover los logs recientes
- Iniciar Apache
- Comprimir los logs movidos
- Ir renombrando los logs como
foo.log.1, foo.log.2… y borrar los más
antiguos
Para usarlo sólo hay que modificar los valores de las variables si no son los correctos. Se puede automatizar con
el programador de tareas de Windows (bastaría con comentar la línea set PAUSA_AL_FINAL=... para que
no se quede la ventana abierta). Y en Windows Vista, naturalmente, requiere que se le asignen al
ejecutarlo privilegios de administrador.
@echo off
chcp 1252 > NUL
setlocal enabledelayedexpansion
set DIR_LOGS=C:\Apache24\logs
set DEF_LOG="%DIR_LOGS%\*access.log" "%DIR_LOGS%\*error.log"
set MAX_LOGS_ANTIGUOS=8
set COMPRIMIR_LOGS=Sí, por favor
set APACHE=C:\Apache24\bin\httpd.exe
set SERVICIO=Apache2.4
set PAUSA_AL_FINAL=Sí, por favor
set SUFIJO_TMP=.log_rotate_tmp-%RANDOM%%RANDOM%
echo ==========================================================================
echo Verificando la configuración de Apache...
echo.
"%APACHE%" -t
echo.
if %ERRORLEVEL% NEQ 0 (
goto cfg_err
) else (
goto cfg_ok
)
:cfg_err
echo Se han encontrado errores, no se puede continuar.
goto fin
:cfg_ok
echo La configuración es correcta.
echo ==========================================================================
echo Deteniendo el servicio "%SERVICIO%"...
echo.
net stop "%SERVICIO%"
if %ERRORLEVEL% NEQ 0 (
goto apagado_err
) else (
goto apagado_ok
)
:apagado_err
echo Ha fallado el apagado del servicio.
goto fin
:apagado_ok
echo El servicio ha sido detenido con éxito.
echo ==========================================================================
echo Moviendo los logs recientes del directorio "%DIR_LOGS%"...
echo.
for %%i in (%DEF_LOG%) do (
set LOG=%%i
set NOMBRE=%%~nxi
set RUTA=%%~dpi
echo !NOMBRE! -^> !NOMBRE!%SUFIJO_TMP%
move /y "!LOG!" "!RUTA!!NOMBRE!%SUFIJO_TMP%" > NUL
)
echo.
echo Los logs han sido movidos con éxito.
echo ==========================================================================
echo Arrancando el servicio "%SERVICIO%"...
echo.
net start "%SERVICIO%"
if %ERRORLEVEL% NEQ 0 (
goto arrancado_err
) else (
goto arrancado_ok
)
:arrancado_err
echo Ha fallado el arrancado del servicio.
goto fin
:arrancado_ok
echo El servicio ha sido arrancado con éxito.
if defined COMPRIMIR_LOGS (
echo ==========================================================================
echo Comprimiendo los logs movidos...
echo.
for %%i in (%DEF_LOG%) do (
set NOMBRE=%%~nxi
set RUTA=%%~dpi
echo !NOMBRE!%SUFIJO_TMP%
compact /c "!RUTA!!NOMBRE!%SUFIJO_TMP%" > NUL
)
echo.
echo Los logs han sido comprimidos con éxito.
echo.
)
echo ==========================================================================
echo Rotando los logs antiguos...
echo.
for %%i in (%DEF_LOG%) do (
set LOG=%%i
set NOMBRE=%%~nxi
set SUFIJO_TMP=%SUFIJO_TMP%
call :rotar
)
goto fin_rotar
:rotar
echo Rotando !NOMBRE!
set /a HASTA=!MAX_LOGS_ANTIGUOS!
if exist "!LOG!.!HASTA!" (
echo Eliminando !NOMBRE!.!HASTA!
del "!LOG!.!HASTA!"
)
:siguiente_rotar
set /a DESDE=!HASTA!-1
if !DESDE! equ 0 (
echo !NOMBRE!!SUFIJO_TMP! -^> !NOMBRE!.!HASTA!
rename "!LOG!!SUFIJO_TMP!" "!NOMBRE!.!HASTA!"
) else (
if exist "!LOG!.!DESDE!" (
echo !NOMBRE!.!DESDE! -^> !NOMBRE!.!HASTA!
rename "!LOG!.!DESDE!" "!NOMBRE!.!HASTA!"
)
)
set /a HASTA=!HASTA!-1
if !HASTA! neq 0 (
goto siguiente_rotar
)
goto :EOF
:fin_rotar
echo.
echo Los logs han sido rotados con éxito.
echo ==========================================================================
:fin
echo.
echo.
if defined PAUSA_AL_FINAL (
pause
);
endlocal
La compresión (que se puede desactivar) sólo funciona si los logs están en una partición NTFS (utiliza el sistema
de compresión integrado en dicho sistema de archivos). Pero… ¿quién ejecuta un servidor web sobre FAT32
en estos tiempos?
Fallos conocidos
- Si se reduce el valor del parámetro
MAX_LOGS_ANTIGUOS cuando ya se había alcanzado esa
cantidad, los archivos más antiguos dejarán de rotar. Solución: eliminar a mano dichos
archivos.
- Si Apache, al ser reiniciado, deja de crear alguno de los logs existentes (por ejemplo, porque se ha
eliminado el sitio web que lo generaba) el último log se rotará con un nombre temporal (example.com-access.log.log_rotate_tmp_31416)
en lugar del nombre correcto (example.com-access.log.1) y los logs archivados no se tocarán.
Solución: renombrar o eliminar el archivo temporal.
Historial
- v2015-11-29
- Actualizar los valores por defecto para Apache/2.4.
- v2014-02-05
- Dar un parámetro más sensato a
chcp. ¿Quién escribe archivos *.bat en ISO-8859-1?
- v2009-03-04
- Limitado el tiempo de apagado del servidor web a lo imprescindible.
- Posibilidad de comprimir los logs archivados (compresión transparente de NTFS).
- Uso de la variable de entorno
%ProgramFiles% en las rutas.
- La pausa final se puede activar/desactivar con una variable.
- v2008-11-25
- El nº de logs ahora se refiere sólo a logs archivados (los *.1, *.2, etc.) en lugar de al total; es más
intuitivo.
- Faltaba el "echo" en uno de los mensajes informativos.
- v2008-08-28
- Primera versión.