Data

4 abr, 2014

Usando MongoDB para armazenar log de aplicações

Publicidade

O MongoDB é uma boa alternativa para armazenar log de servidores. Neste artigo veremos uma solução para centralizar o gerenciamento de logs dos servidores utilizando MongoDB, Log4mongo e Log4j. Com o conector para Hadoop podemos fazer análises complexas dos logs. Essa combinação de NoSQL com Hadoop é muito interessante neste momento em que o big data vem se popularizando. Na segunda parte do artigo há um tutorial de MongoDB, com os passos para instalação e exemplos de consultas.

Os eventos de log são armazenados em JSON, que é um formato adequado para esse tipo de informação. O MongoDB é schemaless, ou seja, você pode adicionar novos campos quando quiser, o que aumenta a flexibilidade da solução. O MongoDB é rápido tanto para inclusão quanto para consultas, suporta milhares de inserts por segundo e, além das queries normais, também podem podemos rodar análises usando JavaScript para executar jobs MapReduce no Hadoop. As vantagens de usar o MongoDB para centralizar os eventos de log dos servidores incluem:

  • Encontrar informações de qualquer aplicação;
  • Estatísticas de acesso e utilização;
  • Erros no histórico da aplicação;
  • Auditoria.

Em um cenário tradicional, os eventos de log são armazenados em texto, gravados no sistema de arquivo em servidores diferentes, de forma que a recuperação de informações pode ser bem complicada e demorada. Não dá para simplesmente consultar todos os eventos de um usuário ou de um módulo em determinado período. Novamente, os arquivos estão distribuídos em vários lugares e têm muitos gigabytes de tamanho. O procedimento normal seria baixar os dados do servidor e procurar com uma ferramenta de texto. É um processo demorado que pode precisar do envolvimento de outras equipes.

Existem casos em que o log é armazenado em banco de dados relacional. A recuperação de eventos de log é mais simples, já que temos o SQL para consultar os dados. Mas há alguns pontos negativos:

  •     Manutenção de mais uma base de dados, que pode ser maior que a base do próprio sistema;
  •     A inclusão de registros é lenta em bases relacionais;
  •     A consulta é lenta. Lembre-se que um sistema médio tem milhões de eventos.

Log4mongo

Vamos utilizar a biblioteca Log4mongo (http://log4mongo.org) para armazenar os eventos de log no MongoDB. Esta biblioteca inclui diversos appenders para o Log4J e com apenas algumas configurações já podemos gravar os eventos no MongoDB.

O Log4mongo tem suporte para diferentes linguagens:

  • Java
  • .Net
  • PHP
  • Python

Como configurar o Log4mongo

Para configurar o Log4mongo no seu projeto, siga os passos abaixo:

  • Faça o download e instale o MongoDB. Está disponível aqui (http://www.mongodb.org/downloads).
  • Inicie o banco de dados com o comando DIR_MONGODB/bin/mongod –dbpath DIR_DADOS.
  • Crie o arquivo de configuração do log4j.properties na sua aplicação com o bloco de código abaixo.

log4j.rootLogger=INFO, MongoDB, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L – %m%n

log4j.appender.MongoDB=org.log4mongo.MongoDbAppender
log4j.appender.MongoDB.databaseName=db-e-commerce
log4j.appender.MongoDB.collectionName=log

log4j.logger.javax.faces=INFO, MongoDB
log4j.logger.org.primefaces=INFO, MongoDB
log4j.logger.com.sun.faces=INFO, MongoDB
log4j.logger.org.hibernate=INFO, MongoDB

Código-fonte

Está disponível no GitHub uma aplicação de exemplo com o código-fonte desse artigo.

Para gerar eventos de log, navegue pela aplicação web ou execute a classe TesteInsereCategoria. Neste projeto serão utilizadas as seguintes tecnologias:

  • Log4mongo
  • JSF 2
  • Primefaces 4
  • JPA 2
  • Tomcat 7

O objetivo é capturar os eventos de log da aplicação. Dessa forma, poderemos dizer quais as páginas mais acessadas, quem alterou um determinado registro e qual o erro mais comum da aplicação.

  • Inclusão, alteração e exclusão de dados;
  • Consultas realizadas;
  • Acesso a páginas;
  • Comandos executados;
  • Exceções disparadas.

Como instalar o MongoDB

Após o download do MongoDB, descompacte o arquivo no seu diretório raiz. Vamos considerar que a instalação está no diretório C:\mongodb. Vamos chamar esse diretório de DIR_MONGODB. Você deve criar um diretório para os dados do banco, então crie o diretório C:\dados\mongodb-data. Este será o diretório DIR_DADOS_MONGODB.

Os executáveis do MongoDB estão em DIR_MONGODB/bin. Acesse esse diretório através do terminal. Os comandos de administração do MongoDB são através de linha de comando mas existem algumas ferramentas úteis aqui.

Como iniciar o MongoDB

Para iniciar o MongoDB, use o comando mongod. Você deve acessar o diretório DIR_MONGODB/bin no console:

mongod --dbpath DIR_DADOS_MONGODB

Para acessar o console de administração do MongoDB:

Mongo

Verifique a resposta do terminal. Deve ser algo similar à listagem abaixo. A partir de agora podemos executar comandos no MongoDB.

MongoDB shell version: 2.4.9

connecting to: test
>

Como criar um database no MongoDB

Não existe a opção de criar uma database no MongoDB. Você pode simplesmente executar o comando “use nome-database” e inserir algum registro. Automaticamente o MongoDB verifica se a base de dados já existe. Se não existir, será criada. O mesmo princípio se aplica às collections, que são como as tabelas de um banco relacional.

Ao iniciar o Tomcat com a nossa aplicação de exemplo, o Filter será inicializado e vai inserir o primeiro registro no banco. Como a base não existia, tanto o banco quanto o esquema serão criados.

A base de dados configurada no Log4mongo se chama db-e-commerce; vamos analisar o que já tem por lá. Para visualizar as bases de dados existentes no seu MongoDB, use o comando:

> show dbs

Caso a collection ainda não tenha sido criada, a resposta do terminal será:

local  0.078125GB
test  (empty)

Se você já iniciou o Tomcat, a base de dados foi criada e a resposta do terminal será:

db-e-commerce  0.203125GB
local  0.078125GB

Para visualizar as coleções que já existem:

> use db-e-commerce
> show collections

Você deve visualizar a collection log, que foi configurada para essa aplicação no log4j.properties.

log
system.indexes

Ou então, para o mesmo resultado:

> show tables

Podemos usar também o comando:

> db.getCollectionNames()
[ "log", "system.indexes" ]

Ou ainda:

> db.system.namespaces.find()

{ "name" : "db-e-commerce.system.indexes" }
{ "name" : "db-e-commerce.log.$_id_" }
{ "name" : "db-e-commerce.log" }

Como criar capped collection no MongoDB

Uma capped collection é uma tabela com tamanho fixo, para evitar estouro no disco. Este comando é opcional, já que estamos apenas fazendo testes.

> db.createCollection('log', {capped:true, size:100000000})

Como fazer consultas no MongoDB

Com os eventos de log centralizados no MongoDB, é bem fácil recuperar informações através das consultas do banco. A linguagem de consulta é bastante completa e a resposta é rápida. Para retornar todos os registros, utilize db.log.find():

> db.log.find()

A resposta será um JSON similar à listagem abaixo:

{ “_id” : ObjectId(“53088a3044ae7fe240c3aac9”), “timestamp” : ISODate(“2014-02-22T11:29:52.221Z”), “level” : “INFO”, “thread” : “localhost-startStop-1”, “message” : “init”, “loggerName” : { “fullyQualifiedClassName” : “net.marcoreis.ecommerce.util.FiltroLogger”, “package” : [  “net”,  “marcoreis”,  “ecommerce”,  “util”,  “FiltroLogger” ], “className” : “FiltroLogger” }, “fileName” : “FiltroLogger.java”, “method” : “init”, “lineNumber” : “20”, “class” : { “fullyQualifiedClassName” : “net.marcoreis.ecommerce.util.FiltroLogger”, “package” : [  “net”,  “marcoreis”,  “ecommerce”,  “util”,  “FiltroLogger” ], “className” : “FiltroLogger” }, “host” : { “process” : “27662@marcoreis-ultrabook”, “name” : “marcoreis-ultrabook”, “ip” : “127.0.0.1” } }

Para consultar os diferentes níveis de erros já gravados, como o “SELECT DISTINCT” do SQL:

> db.log.distinct("level")
[ "INFO", "ERROR", "WARN" ]

Contar quantos registros já foram gravados:

> db.log.count()
416141

Contar quantos registros do tipo INFO:

> db.log.count({"level":"INFO"})
416113

Filtrando o find para mostrar somente o nível ERROR:

> db.log.find({"level":"ERROR"})

O resultado será:

{ “_id” : ObjectId(“53088d4e44ae7fe240c3ab09”), “timestamp” : ISODate(“2014-02-22T11:43:10.751Z”), “level” : “ERROR”, “thread” : “http-bio-8080-exec-7”, “message” : “Usuário inválido”, “loggerName” : { “fullyQualifiedClassName” : “net.marcoreis.ecommerce.controller.BaseBean”, “package” : [  “net”,  “marcoreis”,  “ecommerce”,  “controller”,  “BaseBean” ], “className” : “BaseBean” }, “fileName” : “BaseBean.java”, “method” : “errorMsg”, “lineNumber” : “45”, “class” : { “fullyQualifiedClassName” : “net.marcoreis.ecommerce.controller.BaseBean”, “package” : [  “net”,  “marcoreis”,  “ecommerce”,  “controller”,  “BaseBean” ], “className” : “BaseBean” }, “host” : { “process” : “27662@marcoreis-ultrabook”, “name” : “marcoreis-ultrabook”, “ip” : “127.0.0.1” } }

Registros entre duas datas, como o “BETWEEN” do SQL:

> db.log.find({"timestamp":{$gte:ISODate("2014-02-22"), $lte:ISODate("2014-02-24")}}).count()

Recuperar o primeiro documento da base, como o “ORDER BY”. Se houver muitos registros você deve utilizar o limit ou criar um índice.

> db.log.find().sort({"timestamp":1}).limit(1)

Recuperar o documento mais recente da base:

> db.log.find().sort({"timestamp":1}).limit(-1)

Encontrar todas as páginas JSF que foram acessadas:

> db.log.distinct( "message", {"class.className" : "FiltroLogger" } )

Conclusão

É claro que nenhuma solução de log é perfeita. Todas terão pontos positivos e negativos. A ideia deste artigo foi mostrar uma alternativa usando um banco NoSQL.

Nesta solução com MongoDB, o ponto alto é que temos dados muito detalhados. E podemos adicionar cada vez mais detalhes para conhecer melhor o comportamento da aplicação e do usuário. Através dos comandos de consulta podemos recuperar e analisar os dados de forma centralizada, com uma visão global das aplicações.

Referências