Log managers
1Apresentação
Um log manager é um objeto que recebe as mensagens de log. Isso permite processar os logs de uma forma mais avançada do que simplesmente gravá-los em um arquivo (como faz o framework por padrão).
O uso típico é enviar os logs para um servidor centralizado (Graylog, LogStash, Fluentd, rsyslog, syslog-ng, Nagios, ELK, Datadog…), além de (ou em vez de) gravá-los no arquivo log/temma.log.
2Configuração
No arquivo de configuração etc/temma.php, a diretriz
logManager permite definir um ou vários
objetos que serão chamados para tratar os logs gerados pela aplicação.
Os objetos podem ser colocados em um namespace.
Aqui está um trecho de configuração, que define um manipulador de log:
<?php
return [
'application' => [
'logManager' => 'MyLogManager'
]
];
Também é possível fornecer uma lista de objetos:
<?php
return [
'application' => [
'logManager' => [ 'MyLogManager', '\My\Other\Log\Manager' ]
]
];
Se você quiser usar apenas manipuladores de log, e desativar a gravação no arquivo log/temma.log, deve definir a diretriz logFile como false, null ou atribuir a ela uma string vazia:
<?php
return [
'application' => [
'logFile' => false,
'logManager' => 'MyLogManager',
]
];
3Desenvolvimento simples
Um log manager é um objeto que implementa a interface \Temma\Web\LogManager. Uma instância do objeto é criada quando o framework é iniciado.
O manipulador de log deve conter um método log(), que será chamado a cada nova mensagem de log, recebendo quatro parâmetros:
- Um identificador de rastreamento (trace). É uma string alfanumérica de 4 caracteres, usada para identificar os logs gerados pela mesma requisição.
- O texto da mensagem de log.
- A prioridade da mensagem (DEBUG, NOTE, INFO, …). Pode ser nula.
- A "classe de log" da mensagem. Pode ser nula.
Exemplo:
/** Log manager que grava mensagens em um arquivo. */
class FileLogManager implements \Temma\Web\LogManager {
/**
* Grava os logs da aplicação em um arquivo.
* @param string $traceId Identificador de rastreamento.
* @param string $text Texto da mensagem.
* @param ?string $priority Prioridade da mensagem.
* @param ?string $class Classe de log da mensagem.
*/
public function log(string $traceId, string $text, ?string $priority, ?string $class) : void {
$path = "/var/log/temma/$traceId.log";
$message = "[$priority] ($class) $text";
file_put_contents($path, $message, FILE_APPEND);
}
}
Neste exemplo, o log manager criará um novo arquivo para cada requisição processada pelo framework.
- Linha 2: Definição do objeto.
- Linha 10: Definição do método log(), que será chamado a cada gravação de log.
- Linha 11: Geração do caminho do arquivo.
- Linha 12: Geração da mensagem de texto.
- Linha 13: Grava a mensagem no arquivo. Se o arquivo não existir, ele é criado; caso contrário, a mensagem é adicionada ao final do arquivo.
4Injeção de dependências
Consulte a documentação de injeção de dependências para entender seu propósito e uso no Temma.
Para que um log manager tenha acesso ao componente de injeção de dependências, ele deve implementar a
interface \Temma\Base\Loadable (além da interface \Temma\Web\LogManager).
Ele deverá então implementar um construtor que receba um objeto \Temma\Base\Loader como parâmetro,
por meio do qual poderá acessar outros dados e objetos.
O componente fornece, em particular, acesso aos dados de configuração do framework.
Exemplo:
/**
* Log manager que envia os logs para um serviço externo.
* Uma configuração estendida "x-external-log" deve conter uma chave "url",
* contendo a URL para a qual as mensagens de log serão enviadas.
*/
class ExternalLogManager implements \Temma\Base\Loader, \Temma\Web\LogManager {
/** URL de conexão externa. */
private ?string $_url;
/** Construtor. */
public function __construct(\Temma\Base\Loader $loader) {
// recupera a URL para a qual os logs serão enviados
$this->_url = $loader->config->xtra('external-log', 'url');
}
/** Envia os logs da aplicação para o serviço externo. */
public function log(string $traceId, string $text, ?string $priority, ?string $class) : void {
// verifica os parâmetros de conexão
if (!$this->_url)
return;
// criação da requisição
$ctx = stream_context_create([
'http' => [
'method' => 'POST',
'header' => 'Content-Type: application/x-www-form-urlencoded',
'content' => http_build_query([
'message' => "($traceId) [$priority] ($class) $text",
'hostname' => gethostname(),
]),
],
]);
// envia para o serviço externo
file_get_contents($this->_url, false, $ctx);
}
}
Neste exemplo, o objeto recupera a URL de conexão do serviço externo a partir da configuração e depois a utiliza para enviar as mensagens de log.
5Acesso ao banco de dados
Você deve saber que, na cadeia de inicialização do framework, os manipuladores de log são criados antes dos objetos de conexão com as fontes de dados (isso para que, no caso de um erro durante a criação dessas conexões, a informação apareça nos logs).
Isso significa que, se seu manipulador de log precisar usar uma conexão (por exemplo, para gravar logs em um banco de dados), ele não poderá recuperar a conexão em seu construtor; ele terá que fazer isso em seu método log(), e verificar se a conexão está ativa.
Exemplo:
/** Log manager que grava no banco de dados. */
class DatabaseLogManager implements \Temma\Base\Loader, \Temma\Web\LogManager {
/** Componente de injeção de dependências. */
private \Temma\Base\Loader $_loader;
/** Construtor. Obtém o componente de injeção de dependências. */
public function __construct(\Temma\Base\Loader $loader) {
$this->_loader = $loader;
}
/** Grava os logs da aplicação no banco de dados. */
public function log(string $traceId, string $text, ?string $priority, ?string $class) : void {
// obtém a conexão com o banco de dados
$db = $this->_loader->dataSources->db ?? null;
if (!$db)
return;
// gravação do log
$sql = "INSERT INTO Log
SET log_date = NOW(),
host = " . $db->quote(gethostname()) . ",
message = " . $db->quote($text) . ",
traceId = " . $db->quote($traceId) . ",
priority = " . $db->quote($priority) . ",
class = " . $db->quote($class);
$db->exec($sql);
}
}