Sacar el máximo provecho de sus archivos de registro de PHP: una guía práctica

Publicado: 2022-03-11

Se podría decir con razón que los registros son una de las herramientas más subestimadas y subutilizadas a disposición de un desarrollador de php independiente. A pesar de la gran cantidad de información que pueden ofrecer, no es raro que los registros sean el último lugar donde un desarrollador busca cuando intenta resolver un problema.

En verdad, los archivos de registro de PHP deberían ser en muchos casos el primer lugar para buscar pistas cuando ocurren problemas. A menudo, la información que contienen podría reducir significativamente la cantidad de tiempo que se dedica a tirar de su cabello tratando de rastrear un insecto retorcido.

Pero quizás aún más importante, con un poco de creatividad y previsión, sus archivos de registro se pueden aprovechar para servir como una fuente valiosa de información y análisis de uso. El uso creativo de los archivos de registro puede ayudar a responder preguntas como: ¿Qué navegadores se usan más comúnmente para visitar mi sitio? ¿Cuál es el tiempo de respuesta promedio de mi servidor? ¿Cuál fue el porcentaje de solicitudes a la raíz del sitio? ¿Cómo ha cambiado el uso desde que implementamos las últimas actualizaciones? Y mucho, mucho más.

Archivos de registro de PHP

Este artículo proporciona una serie de consejos sobre cómo configurar sus archivos de registro, así como sobre cómo procesar la información que contienen, para maximizar el beneficio que brindan.

Aunque este artículo se centra técnicamente en el registro para desarrolladores de PHP, gran parte de la información que se presenta aquí es bastante independiente de la tecnología y también es relevante para otros lenguajes y pilas de tecnología.

Nota: este artículo presupone una familiaridad básica con el shell de Unix. Para quienes carezcan de este conocimiento, se proporciona un Apéndice que presenta algunos de los comandos necesarios para acceder y leer archivos de registro en un sistema Unix.

Nuestro proyecto de ejemplo de archivo de registro de PHP

Como proyecto de ejemplo para fines de discusión en este artículo, tomaremos Symfony Standard como un proyecto de trabajo y lo configuraremos en Debian 7 Wheezy con rsyslogd , nginx y PHP-FPM .

 composer create-project symfony/framework-standard-edition my "2.6.*"

Esto nos brinda rápidamente un proyecto de prueba funcional con una interfaz de usuario agradable.

Sugerencias para configurar sus archivos de registro

Aquí hay algunos consejos sobre cómo configurar sus archivos de registro para ayudar a maximizar su valor.

Configuración del registro de errores

Los registros de errores representan la forma más básica de registro; es decir, capturar información adicional y detalles cuando ocurren problemas. Entonces, en un mundo ideal, desearía que no hubiera errores y que sus registros de errores estuvieran vacíos. Pero cuando ocurren problemas (como ocurre invariablemente), sus registros de errores deben ser una de las primeras paradas que haga en su ruta de depuración.

Los registros de errores suelen ser bastante fáciles de configurar.

Por un lado, todos los mensajes de error y bloqueo se pueden registrar en el registro de errores exactamente en el mismo formato en el que, de lo contrario, se presentarían a un usuario. Con una configuración simple, el usuario final nunca necesitará ver esos horribles rastros de error en su sitio, mientras que los desarrolladores aún podrán monitorear el sistema y revisar estos mensajes de error con todo su detalle sangriento. Aquí se explica cómo configurar este tipo de inicio de sesión en PHP:

 log_errors = On error_reporting = E_ALL error_log = /path/to/my/error/log

Otras dos líneas que es importante incluir en un archivo de registro para un sitio en vivo, para evitar que se presenten niveles sangrientos de detalles de error a los usuarios, son:

 display_errors = Off display_startup_errors = Off

Configuración del registro del sistema ( syslog )

Hay muchas implementaciones generalmente compatibles del demonio syslog en el mundo de código abierto, que incluyen:

  • syslogd y sysklogd : se ven con mayor frecuencia en los sistemas de la familia BSD, CentOS, Mac OS X y otros
  • syslog-ng : predeterminado para versiones modernas de Gentoo y SuSE
  • rsyslogd : ampliamente utilizado en las familias de sistemas operativos Debian y Fedora

(Nota: en este artículo, rsyslogd para nuestros ejemplos).

La configuración básica de syslog generalmente es adecuada para capturar sus mensajes de registro en un archivo de registro de todo el sistema (normalmente /var/log/syslog ; también puede ser /var/log/messages o /var/log/system.log según la distribución). estás usando).

El registro del sistema proporciona varias funciones de registro, ocho de las cuales ( LOG_LOCAL0 a LOG_LOCAL7 ) están reservadas para proyectos implementados por el usuario. Aquí, por ejemplo, se muestra cómo puede configurar LOG_LOCAL0 para escribir en 4 archivos de registro separados, según el nivel de registro (es decir, error, advertencia, información, depuración):

 # /etc/rsyslog.d/my.conf local0.err /var/log/my/err.log local0.warning /var/log/my/warning.log local0.info -/var/log/my/info.log local0.debug -/var/log/my/debug.log

Ahora, siempre que escriba un mensaje de registro en la instalación LOG_LOCAL0 , los mensajes de error irán a /var/log/my/err.log , los mensajes de advertencia irán a /var/log/my/warning.log , y así sucesivamente. Tenga en cuenta, sin embargo, que el demonio syslog filtra los mensajes para cada archivo según la regla de "este nivel y superior". Entonces, en el ejemplo anterior, todos los mensajes de error aparecerán en los cuatro archivos configurados, los mensajes de advertencia aparecerán en todos menos en el registro de errores, los mensajes de información aparecerán en los registros de información y depuración, y los mensajes de depuración solo irán a debug.log .

Una nota importante adicional; Los signos - antes de los archivos de nivel de información y depuración en el ejemplo de archivo de configuración anterior indican que las escrituras en esos archivos deben realizarse de forma asíncrona (ya que estas operaciones no bloquean). Por lo general, esto está bien (e incluso se recomienda en la mayoría de las situaciones) para los registros de información y depuración, pero es mejor que las escrituras en el registro de errores (y muy probablemente también en el registro de advertencia) sean sincrónicas.

Para cerrar un nivel de registro menos importante (por ejemplo, en un servidor de producción), simplemente puede redirigir los mensajes relacionados a /dev/null (es decir, a ninguna parte):

 local0.debug /dev/null # -/var/log/my/debug.log

Una personalización específica que es útil, especialmente para admitir algunos de los análisis de archivos de registro de PHP que analizaremos más adelante en este artículo, es usar la pestaña como carácter delimitador en los mensajes de registro. Esto se puede hacer fácilmente agregando el siguiente archivo en /etc/rsyslog.d :

 # /etc/rsyslog.d/fixtab.conf $EscapeControlCharactersOnReceive off

Y, por último, no olvide reiniciar el demonio syslog después de realizar cambios en la configuración para que surtan efecto:

 service rsyslog restart

Configuración de registro del servidor

A diferencia de los registros de aplicaciones y los registros de errores en los que puede escribir, los registros del servidor los escriben exclusivamente los demonios del servidor correspondientes (por ejemplo, servidor web, servidor de base de datos, etc.) en cada solicitud. El único "control" que tiene sobre estos registros es en la medida en que el servidor le permite configurar su funcionalidad de registro. Aunque puede haber mucho que analizar en estos archivos, a menudo son la única forma de tener una idea clara de lo que sucede "bajo el capó" de su servidor.

Implementemos nuestra aplicación de ejemplo Symfony Standard en un entorno nginx con backend de almacenamiento MySQL. Aquí está la configuración del host nginx que usaremos:

 server { server_name my.log-sandbox; root /var/www/my/web; location / { # try to serve file directly, fallback to app.php try_files $uri /app.php$is_args$args; } # DEV # This rule should only be placed on your development environment # In production, don't include this and don't deploy app_dev.php or config.php location ~ ^/(app_dev|config)\.php(/|$) { fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_split_path_info ^(.+\.php)(/.*)$; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param HTTPS off; } # PROD location ~ ^/app\.php(/|$) { fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_split_path_info ^(.+\.php)(/.*)$; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param HTTPS off; # Prevents URIs that include the front controller. This will 404: # http://domain.tld/app.php/some-path # Remove the internal directive to allow URIs like this internal; } error_log /var/log/nginx/my_error.log; access_log /var/log/nginx/my_access.log; }

Con respecto a las dos últimas directivas anteriores: access_log representa el registro de solicitudes generales, mientras que error_log es para errores y, al igual que con los registros de errores de la aplicación, vale la pena configurar un monitoreo adicional para recibir alertas sobre problemas y poder reaccionar rápidamente.

Nota: Este es un archivo de configuración nginx intencionalmente simplificado que se proporciona solo con fines de ejemplo. Casi no presta atención a la seguridad y el rendimiento y no debe usarse tal cual en ningún entorno "real".

Esto es lo que obtenemos en /var/log/nginx/my_access.log después de escribir http://my.log-sandbox/app_dev.php/ en el navegador y presionar Enter .

 192.168.56.1 - - [26/Apr/2015:16:13:28 +0300] "GET /app_dev.php/ HTTP/1.1" 200 6715 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36" 192.168.56.1 - - [26/Apr/2015:16:13:28 +0300] "GET /bundles/framework/css/body.css HTTP/1.1" 200 6657 "http://my.log-sandbox/app_dev.php/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36" 192.168.56.1 - - [26/Apr/2015:16:13:28 +0300] "GET /bundles/framework/css/structure.css HTTP/1.1" 200 1191 "http://my.log-sandbox/app_dev.php/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36" 192.168.56.1 - - [26/Apr/2015:16:13:28 +0300] "GET /bundles/acmedemo/css/demo.css HTTP/1.1" 200 2204 "http://my.log-sandbox/app_dev.php/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36" 192.168.56.1 - - [26/Apr/2015:16:13:28 +0300] "GET /bundles/acmedemo/images/welcome-quick-tour.gif HTTP/1.1" 200 4770 "http://my.log-sandbox/app_dev.php/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36" 192.168.56.1 - - [26/Apr/2015:16:13:28 +0300] "GET /bundles/acmedemo/images/welcome-demo.gif HTTP/1.1" 200 4053 "http://my.log-sandbox/app_dev.php/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36" 192.168.56.1 - - [26/Apr/2015:16:13:28 +0300] "GET /bundles/acmedemo/images/welcome-configure.gif HTTP/1.1" 200 3530 "http://my.log-sandbox/app_dev.php/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36" 192.168.56.1 - - [26/Apr/2015:16:13:28 +0300] "GET /favicon.ico HTTP/1.1" 200 6518 "http://my.log-sandbox/app_dev.php/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36" 192.168.56.1 - - [26/Apr/2015:16:13:30 +0300] "GET /app_dev.php/_wdt/e50d73 HTTP/1.1" 200 13265 "http://my.log-sandbox/app_dev.php/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36"

Esto muestra que, para servir una página, el navegador en realidad realiza 9 llamadas HTTP. 7 de ellos, sin embargo, son solicitudes de contenido estático, que son simples y ligeros. Sin embargo, todavía toman recursos de la red y esto es lo que se puede optimizar usando varios sprites y técnicas de minificación.

Si bien esas optimizaciones se discutirán en otro artículo, lo relevante aquí es que podemos registrar solicitudes a contenidos estáticos por separado usando otra directiva de location para ellos:

 location ~ \.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js)$ { access_log /var/log/nginx/my_access-static.log; }

Recuerde que la location de nginx realiza una coincidencia simple de expresiones regulares, por lo que puede incluir tantas extensiones de contenido estático como espere enviar en su sitio.

El análisis de dichos registros no es diferente al análisis de los registros de aplicaciones.

Otros registros que vale la pena mencionar

Otros dos registros de PHP que vale la pena mencionar son el registro de depuración y el registro de almacenamiento de datos.

El registro de depuración

Otra cosa conveniente sobre los registros de nginx es el registro de depuración. Podemos activarlo reemplazando la línea error_log de la configuración con lo siguiente (requiere que se instale el módulo de depuración nginx):

 error_log /var/log/nginx/my_error.log debug;

La misma configuración se aplica a Apache o cualquier otro servidor web que utilice.

Y, por cierto, los registros de depuración no están relacionados con los registros de errores, aunque estén configurados en la directiva error_log .

Aunque el registro de depuración puede ser detallado (¡una sola solicitud de nginx, por ejemplo, generó 127 KB de datos de registro!), todavía puede ser muy útil. Navegar por un archivo de registro puede ser engorroso y tedioso, pero a menudo puede proporcionar rápidamente pistas e información que ayudan en gran medida a acelerar el proceso de depuración.

En particular, el registro de depuración realmente puede ayudar a depurar las configuraciones de nginx, especialmente las partes más complicadas, como la coincidencia de location y las cadenas de rewrite .

Por supuesto, los registros de depuración nunca deben habilitarse en un entorno de producción. La cantidad de espacio que utilizan también y la cantidad de información que almacenan significa una gran carga de E/S en su servidor, lo que puede degradar significativamente el rendimiento de todo el sistema.

Registros de almacenamiento de datos

Otro tipo de registro del servidor (útil para la depuración) son los registros de almacenamiento de datos. En MySQL, puede activarlos agregando estas líneas:

 [mysqld] general_log = 1 general_log_file = /var/log/mysql/query.log

Estos registros simplemente contienen una lista de consultas ejecutadas por el sistema mientras atiende las solicitudes de la base de datos en orden cronológico, lo que puede ser útil para diversas necesidades de depuración y seguimiento. Sin embargo, no deben permanecer habilitados en los sistemas de producción, ya que generarán una carga de E/S adicional innecesaria, lo que afecta el rendimiento.

Escribir en sus archivos de registro

El propio PHP proporciona funciones para abrir, escribir y cerrar archivos de registro ( openlog() , syslog() y closelog() , respectivamente).

También existen numerosas bibliotecas de registro para el desarrollador de PHP, como Monolog (popular entre los usuarios de Symfony y Laravel), así como varias implementaciones específicas del marco, como las capacidades de registro incorporadas en CakePHP. En general, las bibliotecas como Monolog no solo envuelven las llamadas a syslog() , sino que también permiten usar otras funciones y herramientas de back-end.

Aquí hay un ejemplo simple de cómo escribir en el registro:

 <?php openlog(uniqid(), LOG_ODELAY, LOG_LOCAL0); syslog(LOG_INFO, 'It works!');

Nuestra llamada aquí para openlog :

  • configura PHP para anteponer un identificador único a cada mensaje de registro del sistema durante la vida útil del script
  • lo configura para retrasar la apertura de la conexión syslog hasta que se haya producido la primera llamada syslog()
  • establece LOG_LOCAL0 como la instalación de registro predeterminada

Así es como se vería el contenido del archivo de registro después de ejecutar el código anterior:

 # cat /var/log/my/info.log Mar 2 00:23:29 log-sandbox 54f39161a2e55: It works!

Maximizando el valor de sus archivos de registro de PHP

Ahora que todos estamos bien con la teoría y los conceptos básicos, veamos cuánto podemos obtener de los registros haciendo la menor cantidad de cambios posible en nuestro proyecto de muestra Symfony Standard.

Primero, creemos los scripts src/log-begin.php (para abrir y configurar correctamente nuestros registros) y src/log-end.php (para registrar información sobre la finalización exitosa). Tenga en cuenta que, para simplificar, solo escribiremos todos los mensajes en el registro de información.

 # src/log-begin.php <?php define('START_TIME', microtime(true)); openlog(uniqid(), LOG_ODELAY, LOG_LOCAL0); syslog(LOG_INFO, 'BEGIN'); syslog(LOG_INFO, "URI\t{$_SERVER['REQUEST_URI']}"); $browserHash = substr(md5($_SERVER['HTTP_USER_AGENT']), 0, 7); syslog(LOG_INFO, "CLIENT\t{$_SERVER['REMOTE_ADDR']}\t{$browserHash}"); <br /> # src/log-end.php <?php syslog(LOG_INFO, "DISPATCH TIME\t" . round(microtime(true) - START_TIME, 2)); syslog(LOG_INFO, 'END');

Y exijamos estos scripts en app.php :

 <?php require_once(dirname(__DIR__) . '/src/log-begin.php'); syslog(LOG_INFO, "MODE\tPROD"); # original app.php contents require_once(dirname(__DIR__) . '/src/log-end.php');

Para el entorno de desarrollo, también queremos requerir estos scripts en app_dev.php . El código para hacerlo sería el mismo que el anterior, excepto que estableceríamos MODE en DEV en lugar de PROD .

También queremos rastrear qué controladores se están invocando, así que agreguemos una línea más en Acme\DemoBundle\EventListener\ControllerListener , justo al comienzo del método ControllerListener::onKernelController() :

 syslog(LOG_INFO, "CONTROLLER\t" . get_class($event->getController()[0]));

Tenga en cuenta que estos cambios totalizan solo 15 líneas adicionales de código, pero colectivamente pueden generar una gran cantidad de información.

Análisis de los datos en sus archivos de registro

Para empezar, veamos cuántas solicitudes HTTP se requieren para atender cada carga de página.

Aquí está la información en los registros para una solicitud, según la forma en que configuramos nuestro registro:

 Mar 3 12:04:20 log-sandbox 54f58724b1ccc: BEGIN Mar 3 12:04:20 log-sandbox 54f58724b1ccc: URI /app_dev.php/ Mar 3 12:04:20 log-sandbox 54f58724b1ccc: CLIENT 192.168.56.1 1b101cd Mar 3 12:04:20 log-sandbox 54f58724b1ccc: MODE DEV Mar 3 12:04:23 log-sandbox 54f58724b1ccc: CONTROLLER Acme\DemoBundle\Controller\WelcomeController Mar 3 12:04:25 log-sandbox 54f58724b1ccc: DISPATCH TIME 4.51 Mar 3 12:04:25 log-sandbox 54f58724b1ccc: END Mar 3 12:04:25 log-sandbox 54f5872967dea: BEGIN Mar 3 12:04:25 log-sandbox 54f5872967dea: URI /app_dev.php/_wdt/59b8b6 Mar 3 12:04:25 log-sandbox 54f5872967dea: CLIENT 192.168.56.1 1b101cd Mar 3 12:04:25 log-sandbox 54f5872967dea: MODE DEV Mar 3 12:04:28 log-sandbox 54f5872967dea: CONTROLLER Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController Mar 3 12:04:29 log-sandbox 54f5872967dea: DISPATCH TIME 4.17 Mar 3 12:04:29 log-sandbox 54f5872967dea: END

Así que ahora sabemos que cada carga de página en realidad se sirve con dos solicitudes HTTP.

En realidad, hay dos puntos que vale la pena mencionar aquí. Primero, las dos solicitudes por carga de página son para usar Symfony en modo desarrollador (que he hecho a lo largo de este artículo). Puede identificar las llamadas al modo de desarrollo buscando fragmentos de URL /app-dev.php/ . En segundo lugar, digamos que cada carga de página se realiza con dos solicitudes posteriores a la aplicación Symfony. Como vimos anteriormente en los registros de acceso de nginx, en realidad hay más llamadas HTTP, algunas de las cuales son para contenido estático.

Bien, ahora naveguemos un poco en el sitio de demostración (para acumular los datos en los archivos de registro) y veamos qué más podemos aprender de estos registros.

¿Cuántas solicitudes se atendieron en total desde el comienzo del archivo de registro?

 # grep -c BEGIN info.log 10

¿Alguno de ellos falló (se cerró el guión sin llegar al final)?

 # grep -c END info.log 10

Vemos que la cantidad de registros BEGIN y END coinciden, por lo que esto nos dice que todas las llamadas fueron exitosas. (Si la secuencia de comandos PHP no se hubiera completado correctamente, no habría llegado a la ejecución de la secuencia de comandos src/log-end.php ).

¿Cuál fue el porcentaje de solicitudes a la raíz del sitio?

 # `grep -cE "\s/app_dev.php/$" info.log` 2

Esto nos dice que hubo 2 cargas de página de la raíz del sitio. Como supimos anteriormente que (a) hay 2 solicitudes a la aplicación por carga de página y (b) hubo un total de 10 solicitudes HTTP, el porcentaje de solicitudes a la raíz del sitio fue del 40 % (es decir, 2x2/10).

¿Qué clase de controlador es responsable de atender las solicitudes a la raíz del sitio?

 # grep -E "\s/$|\s/app_dev.php/$" info.log | head -n1 Mar 3 12:04:20 log-sandbox 54f58724b1ccc: URI /app_dev.php/ # grep 54f58724b1ccc info.log | grep CONTROLLER Mar 3 12:04:23 log-sandbox 54f58724b1ccc: CONTROLLER Acme\DemoBundle\Controller\WelcomeController

Aquí usamos la identificación única de una solicitud para verificar todos los mensajes de registro relacionados con esa única solicitud. Por lo tanto, pudimos determinar que la clase de controlador responsable de atender las solicitudes a la raíz del sitio es Acme\DemoBundle\Controller\WelcomeController .

¿Qué clientes con IPs de subred 192.168.0.0/16 han accedido al sitio?

 # grep CLIENT info.log | cut -d":" -f4 | cut -f2 | sort | uniq 192.168.56.1

Como era de esperar en este caso de prueba simple, solo mi computadora host ha accedido al sitio. Este es, por supuesto, un ejemplo muy simple, pero la capacidad que demuestra (de poder analizar las fuentes del tráfico a su sitio) es obviamente bastante poderosa e importante.

¿Cuánto del tráfico a mi sitio proviene de FireFox?

Teniendo 1b101cd como el hash de mi agente de usuario de Firefox, puedo responder a esta pregunta de la siguiente manera:

 # grep -c 1b101cd info.log 8 # grep -c CLIENT info.log 10

Respuesta: 80% (es decir, 8/10)

¿Cuál es el porcentaje de solicitudes que arrojaron una respuesta lenta?

Para los propósitos de este ejemplo, definiremos "lento" como tomar más de 5 segundos para dar una respuesta. Respectivamente:

 # grep "DISPATCH TIME" info.log | grep -cE "\s[0-9]{2,}\.|\s[5-9]\." 2

Respuesta: 20% (es decir, 2/10)

¿Alguna vez alguien suministró parámetros GET?

 # grep URI info.log | grep \?

No, el estándar de Symfony solo usa slugs de URL, por lo que esto también nos dice aquí que nadie ha intentado piratear el sitio.

Estos son solo algunos ejemplos relativamente rudimentarios de las formas en que los archivos de registro se pueden aprovechar de forma creativa para generar información de uso valiosa e incluso análisis básicos.

Otras cosas a tener en cuenta

Mantener las cosas seguras

Otro aviso es por seguridad. Puede pensar que registrar solicitudes es una buena idea, en la mayoría de los casos, de hecho lo es. Sin embargo, es importante tener mucho cuidado al eliminar cualquier información de usuario potencialmente confidencial antes de almacenarla en el registro.

Lucha contra la hinchazón del archivo de registro

Dado que los archivos de registro son archivos de texto a los que siempre agrega información, crecen constantemente. Dado que este es un problema bien conocido, existen algunos enfoques bastante estándar para controlar el crecimiento de los archivos de registro.

Lo más fácil es rotar los troncos . Rotar troncos significa:

  • Reemplazar periódicamente el registro con un nuevo archivo vacío para seguir escribiendo
  • Almacenamiento del archivo antiguo para el historial
  • Eliminación de archivos que han "envejecido" lo suficiente como para liberar espacio en disco
  • Asegurarse de que la aplicación pueda escribir en los registros sin interrupciones cuando se produzcan estos cambios en los archivos

La solución más común para esto es logrotate , que viene preinstalado con la mayoría de las distribuciones *nix. Veamos un archivo de configuración simple para rotar nuestros registros:

 /var/log/my/debug.log /var/log/my/info.log /var/log/my/warning.log /var/log/my/error.log { rotate 7 daily missingok notifempty delaycompress compress sharedscripts postrotate invoke-rc.d rsyslog rotate > /dev/null endscript }

Otro enfoque más avanzado es hacer que rsyslogd mismo escriba mensajes en archivos, creados dinámicamente en función de la fecha y hora actuales. Esto aún requeriría una solución personalizada para la eliminación de archivos más antiguos, pero permite que los desarrolladores administren los plazos para cada archivo de registro con precisión. Para nuestro ejemplo:

 $template DynaLocal0Err, "/var/log/my/error-%$NOW%-%$HOUR%.log" $template DynaLocal0Info, "/var/log/my/info-%$NOW%-%$HOUR%.log" $template DynaLocal0Warning, "/var/log/my/warning-%$NOW%-%$HOUR%.log" $template DynaLocal0Debug, "/var/log/my/debug-%$NOW%-%$HOUR%.log" local1.err -?DynaLocal0Err local1.info -?DynaLocal0Info local1.warning -?DynaLocal0Warning local1.debug -?DynaLocal0Debug

De esta forma, rsyslog creará un archivo de registro individual cada hora y no habrá necesidad de rotarlos y reiniciar el demonio. Así es como se pueden eliminar los archivos de registro de más de 5 días para lograr esta solución:

 find /var/log/my/ -mtime +5 -print0 | xargs -0 rm

Registros remotos

A medida que el proyecto crece, el análisis de la información de los registros consume cada vez más recursos. Esto no solo significa crear una carga adicional en el servidor; también significa crear una carga máxima en la CPU y las unidades de disco en los momentos en que analiza los registros, lo que puede degradar el tiempo de respuesta del servidor para los usuarios (o, en el peor de los casos, incluso puede provocar la caída del sitio).

Para resolver esto, considere configurar un servidor de registro centralizado . Todo lo que necesita para esto es otra caja con el puerto UDP 514 (predeterminado) abierto. Para hacer que rsyslogd escuche las conexiones, agregue la siguiente línea a su archivo de configuración:

 $UDPServerRun 514

Teniendo esto, configurar el cliente es tan fácil como:

 *.* @HOSTNAME:514

(donde HOSTNAME es el nombre de host de su servidor de registro remoto).

Conclusión

Si bien este artículo ha demostrado algunas de las formas creativas en que los archivos de registro pueden ofrecer información mucho más valiosa de lo que podría haber imaginado anteriormente, es importante enfatizar que solo hemos arañado la superficie de lo que es posible. La extensión, el alcance y el formato de lo que puede registrar es casi ilimitado. Esto significa que, si hay datos de uso o análisis que desea extraer de sus registros, simplemente debe registrarlos de una manera que facilite su posterior análisis y análisis. Además, ese análisis a menudo se puede realizar con herramientas de línea de comandos estándar de Linux como grep , sed o awk .

De hecho, los archivos de registro de PHP son una herramienta muy poderosa que puede ser de gran beneficio.

Recursos

Código en GitHub: https://github.com/isanosyan/toptal-blog-logs-post-example


Apéndice: lectura y manipulación de archivos de registro en el shell de Unix

Aquí hay una breve introducción a algunas de las herramientas de línea de comando *nix más comunes con las que querrá familiarizarse para leer y manipular sus archivos de registro.

  • cat es quizás el más simple. Imprime todo el archivo en el flujo de salida. Por ejemplo, el siguiente comando imprimirá logfile1 en la consola:

     cat logfile1
  • > El carácter permite al usuario redirigir la salida, por ejemplo, a otro archivo. Abre el flujo de destino en modo de escritura (lo que significa borrar los contenidos de destino). Así es como reemplazamos el contenido de tmpfile con el contenido de logfile1 :

     cat logfile1 > tmpfile
  • >> redirige la salida y abre el flujo de destino en modo de adición. El contenido actual del archivo de destino se conservará, se agregarán nuevas líneas al final. Esto logfile1 contenido de logfile1 a tmpfile :

     cat logfile1 >> tmpfile
  • grep filtra el archivo por algún patrón e imprime solo las líneas coincidentes. El siguiente comando solo imprimirá líneas del logfile1 de registro 1 que contenga el mensaje de Bingo :

     grep Bingo logfile1
  • cut imprime el contenido de una sola columna (por número a partir de 1). De forma predeterminada, busca caracteres de tabulación como delimitadores entre columnas. Por ejemplo, si tiene un archivo lleno de marcas de tiempo en formato YYYY-MM-DD HH:MM:SS , esto le permitirá imprimir solo años:

     cut -d"-" -f1 logfile1
  • head muestra solo las primeras líneas de un archivo

  • tail muestra solo las últimas líneas de un archivo

  • sort ordena líneas en la salida

  • uniq filtra líneas duplicadas

  • wc cuenta palabras (o líneas cuando se usa con el indicador -l )

  • | (es decir, el símbolo de la "tubería") proporciona la salida de un comando como entrada para el siguiente. Pipe es muy conveniente para combinar comandos. Por ejemplo, así es como podemos encontrar meses de 2014 que ocurren dentro de un conjunto de marcas de tiempo:

     grep -E "^2014" logfile1 | cut -d"-" -f2 | sort | uniq

Aquí primero hacemos coincidir las líneas con la expresión regular "comienza con 2014", luego cortamos los meses. Finalmente, usamos una combinación de sort y uniq para imprimir las ocurrencias solo una vez.