Recopilación de logs de PHP
Para enviar tus logs de PHP a Datadog, loguea un archivo y luego supervisa ese archivo con tu Datadog Agent. En esta página, se detallan ejemplos de configuración para las bibliotecas de registro Monolog, Zend-Log y Symfony.
Configuración
Instalación
Ejecuta este comando para utilizar Composer para añadir Monolog como una dependencia:
composer require "monolog/monolog"
Alternativamente, instala Monolog manualmente de la siguiente manera:
Descarga Monolog del repositorio e inclúyelo en las bibliotecas.
Añade lo siguiente en el arranque de la aplicación para inicializar la instancia:
<?php
require __DIR__ . '/vendor/autoload.php';
// load Monolog library
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Formatter\JsonFormatter;
Zend-Log forma parte del marco Zend. Ejecuta este comando para utilizar Composer para añadir Zend-Log:
composer require "zendframework/zend-log"
Alternativamente, instala Zend-Log manualmente de la siguiente manera:
- Descarga la fuente del repositorio e inclúyela en las bibliotecas.
- Añade lo siguiente al arranque de la aplicación para inicializar la instancia:
<?php
require __DIR__ . '/vendor/autoload.php';
use Zend\Log\Logger;
use Zend\Log\Writer\Stream;
use Zend\Log\Formatter\JsonFormatter;
Añade lo siguiente para declarar un formateador JSON de Monolog como un servicio:
services:
monolog.json_formatter:
class: Monolog\Formatter\JsonFormatter
Configurar tu logger
La siguiente configuración habilita el formato JSON y redacta los logs y eventos en el archivo application-json.log
. En tu código, añade un nuevo manejador después de la inicialización de la instancia de Monolog:
<?php
require __DIR__ . '/vendor/autoload.php';
// cargar biblioteca Monolog
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Formatter\JsonFormatter;
// crear un canal de log
$log = new Logger('channel_name');
// crear un formateador de Json
$formatter = new JsonFormatter();
// crear un manejador
$stream = new StreamHandler(__DIR__.'/application-json.log', Logger::DEBUG);
$stream->setFormatter($formatter);
// vincular
$log->pushHandler($stream);
// un ejemplo
$log->info('Adding a new user', array('username' => 'Seldaek'));
La siguiente configuración habilita el formato JSON y redacta los logs y eventos en el archivo application-json.log
. En tu código, añade un nuevo manejador después de la inicialización de la instancia de Zend-Log:
<?php
use Zend\Log\Logger;
use Zend\Log\Writer\Stream;
use Zend\Log\Formatter\JsonFormatter;
// crear un registrador
$logger = new Logger();
// crear un escritor
$writer = new Stream('file://' . __DIR__ . '/application-json.log');
// crear un formateador Json
$formatter = new JsonFormatter();
$writer->setFormatter($formatter);
// vincular
$logger->addWriter($writer);
Zend\Log\Logger::registerErrorHandler($logger);
Para configurar el formateador en tu configuración de Monolog, declara el campo de formateador de la siguiente manera:
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
formatter: monolog.json_formatter
Configurar el Datadog Agent
Cuando tengas la recopilación de logs activada, haz la siguiente configuración de la recopilación de logs personalizada para supervisar tus archivos de log y enviar logs nuevos a Datadog.
- Crea una carpeta
php.d/
en el directorio de configuración del Agent conf.d/
. - Crea un archivo
conf.yaml
en php.d/
con el siguiente contenido:
init_config:
instances:
## Sección de log
logs:
- type: file
path: "<path_to_your_php_application_json>.log"
service: "<service_name>"
source: php
sourcecategory: sourcecode
Conectar tus servicios entre logs y trazas (traces)
Si APM está habilitado para esta aplicación, la correlación entre los logs de aplicación y trazas puede mejorarse siguiendo las instrucciones de registro de APM PHP para añadir automáticamente trazas e IDs de tramos en tus logs.
Añadir más contexto a los logs
Puede ser útil añadir contexto adicional a tus logs y eventos. Monolog proporciona métodos para establecer el contexto local del subproceso que luego se envía automáticamente con todos los eventos. Por ejemplo, para loguear un evento con datos contextuales:
<?php
$logger->info('Adding a new user', array('username' => 'Seldaek'));
El preprocesador de Monolog tiene una función que es una devolución de llamada simple y enriquece tus eventos con metadatos que puedes establecer (por ejemplo, el ID de sesión, o el ID de solicitud):
<?php
$log->pushProcessor(function ($record) {
// registrat el usuario actual
$user = Acme::getCurrentUser();
$record['context']['user'] = array(
'name' => $user->getName(),
'username' => $user->getUsername(),
'email' => $user->getEmail(),
);
// Añadir varias etiquetas
$record['ddtags'] = array('key' => 'value');
// Añadir varios contextos genéricos
$record['extra']['key'] = 'value';
return $record;
});
Puede ser útil añadir contexto adicional a tus logs y eventos. Zend-Log proporciona métodos para establecer el contexto local del subproceso que luego se envía automáticamente con todos los eventos. Por ejemplo, para loguear un evento con datos contextuales:
<?php
$logger->info('Adding a new user', array('username' => 'Seldaek'));
Consulta la documentación del procesador de Zend para obtener más información sobre cómo proporcionar información adicional a tus logs.
Sigue estos pasos para añadir contexto variable en tus logs utilizando un procesador de sesión.
Implementar tu procesador de sesiones:
En el siguiente ejemplo, el procesador conoce la sesión actual y mejora el contenido del registro de log con información como requestId
, sessionId
, etc.
<?php
namespace Acme\Bundle\MonologBundle\Log;
use Symfony\Component\HttpFoundation\Session\Session;
class SessionRequestProcessor {
private $session;
private $sessionId;
private $requestId;
private $_server;
private $_get;
private $_post;
public function __construct(Session $session) {
$this->session = $session;
}
public function processRecord(array $record) {
if (null === $this->requestId) {
if ('cli' === php_sapi_name()) {
$this->sessionId = getmypid();
} else {
try {
$this->session->start();
$this->sessionId = $this->session->getId();
} catch (\RuntimeException $e) {
$this->sessionId = '????????';
}
}
$this->requestId = substr(uniqid(), -8);
$this->_server = array(
'http.url' => (@$_SERVER['HTTP_HOST']).'/'.(@$_SERVER['REQUEST_URI']),
'http.method' => @$_SERVER['REQUEST_METHOD'],
'http.useragent' => @$_SERVER['HTTP_USER_AGENT'],
'http.referer' => @$_SERVER['HTTP_REFERER'],
'http.x_forwarded_for' => @$_SERVER['HTTP_X_FORWARDED_FOR']
);
$this->_post = $this->clean($_POST);
$this->_get = $this->clean($_GET);
}
$record['http.request_id'] = $this->requestId;
$record['http.session_id'] = $this->sessionId;
$record['http.url'] = $this->_server['http.url'];
$record['http.method'] = $this->_server['http.method'];
$record['http.useragent'] = $this->_server['http.useragent'];
$record['http.referer'] = $this->_server['http.referer'];
$record['http.x_forwarded_for'] = $this->_server['http.x_forwarded_for'];
return $record;
}
protected function clean($array) {
$toReturn = array();
foreach(array_keys($array) as $key) {
if (false !== strpos($key, 'password')) {
// Do not add
} else if (false !== strpos($key, 'csrf_token')) {
// Do not add
} else {
$toReturn[$key] = $array[$key];
}
}
return $toReturn;
}
}
Integrar el procesador con Symfony añadiendo lo siguiente:
services:
monolog.processor.session_request:
class: Acme\Bundle\MonologBundle\Log\SessionRequestProcessor
arguments: [ @session ]
tags:
- { name: monolog.processor, method: processRecord }
Envía en un flujo el archivo JSON generado a Datadog.
Integración del marco de Monolog
Monolog puede utilizarse con los siguientes marcos:
Para integrar Monolog con tu marco, añade lo siguiente:
<?php
// Comprueba si la biblioteca de Monolog está bien cargada
//use Monolog\Logger;
//use Monolog\Handler\StreamHandler;
//use Monolog\Formatter\JsonFormatter;
// con la instancia de monolog
$monolog = ...
///// Configuración de envío de log
$formatter = new JsonFormatter();
$stream = new StreamHandler(__DIR__.'/application-json.log', Logger::DEBUG);
$stream->setFormatter($formatter);
$monolog->pushHandler($stream);
return $r;
A continuación, configura tu registrador para Monolog.
En tu directorio de configuración /path/to/config/directory/
, añade lo siguiente a config_dev.yml
y config_prod.yml
. Modifica el ejemplo para configurarlo para tus entornos de desarrollo y producción.
# app/config/config.yml
monolog:
# Deja sin comentarios esta sección si deseas usar un procesador
# Processor :
# session_processor:
# class: Acme\Bundle\MonologBundle\Log\SessionRequestProcessor
# arguments: [ @session ]
# tags:
# - { name: monolog.processor, method: processRecord }
json_formatter:
class: Monolog\Formatter\JsonFormatter
handlers:
# Configuración de envío de log
to_json_files:
# log to var/logs/(environment).log
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
# includes all channels (doctrine, errors, and so on)
channels: ~
# use json formatter
formatter: monolog.json_formatter
# set the log level (for example: debug, error, or alert)
level: debug
En tu directorio de configuración /path/to/config/directory/
, añade lo siguiente a config_dev.yml
y config_prod.yml
. Modifica el ejemplo para configurarlo para tus entornos de desarrollo y producción.
monolog:
handlers:
# Configuración de envío de log
to_json_files:
# log to var/logs/(environment).log
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
# use json formatter
formatter: monolog.json_formatter
# set the log level (for example: debug, error, or alert)
level: debug
La función
\DDTrace\current_context()
se presentó en la versión
0.61.0.
Añade lo siguiente:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
// Get the Monolog instance
$monolog = logger()->getLogger();
if (!$monolog instanceof \Monolog\Logger) {
return;
}
// Opcional: utilice el formato JSON
$useJson = false;
foreach ($monolog->getHandlers() as $handler) {
if (method_exists($handler, 'setFormatter')) {
$handler->setFormatter(new \Monolog\Formatter\JsonFormatter());
$useJson = true;
}
}
// Inyecta la traza y el ID de tramo para conectar la entrada de log con la traza de APM
$monolog->pushProcessor(function ($record) use ($useJson) {
$context = \DDTrace\current_context();
if ($useJson === true) {
$record['extra']['dd'] = [
'trace_id' => $context['trace_id'],
'span_id' => $context['span_id'],
];
} else {
$record['message'] .= sprintf(
' [dd.trace_id=%d dd.span_id=%d]',
$context['trace_id'],
$context['span_id']
);
}
return $record;
});
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//
}
}
Añade lo siguiente:
<?php
// file: bootstrap
$app->extend('monolog', function($monolog, $app) {
$monolog->pushHandler(...);
// configure your logger below
return $monolog;
});
Añade lo siguiente:
<?php
//file: bootstrap/app.php
$app->configureMonologUsing(function($monolog) {
$monolog->pushHandler(...);
// configure your logger below
});
return $app;
Leer más
Más enlaces, artículos y documentación útiles: