3 min read

Application Logging through RabbitMQ and Graylog

Centralizing your Grails application logging allows for more robust log management and easier issue resolution.
Application Logging through RabbitMQ and Graylog
Photo by Jake Walker / Unsplash

Tracking multiple log application logs on multiple servers can be a hassle.

Centralizing your application logging allows for more robust log management and easier issue resolution.

Graylog is an open-source log management tool built upon Elasticsearch and MongoDB that allows you to:

"Store, search & analyze log data from any source." -- www.graylog.org

For this tutorial, we're running everything under a single host under Docker containers. Once you understand the flow and underlying technologies this can easily be scaled out.

Deploy RabbitMQ

Graylog provides many ways to feed logging data into its index; HTTP, TCP, UDP, AMPQ, Kafka, Flume to name a few.

I use AMPQ  for most of our projects because we have already built out large RabbitMQ clusters so it's a technology we use and support for other projects. AMPQ is supported by most of the logging appenders and allows for the mobility that log shipping tools don't and security that TCP and UDP don't easily provide.

So we'll first deploy RabbitMQ container for log data routing. We'll launch it with exposed ports 5672 (AMQP) and 15672 (Admin interface)

$> docker run -d --name rabbitmq \ 
     -p 5672:5672 \ 
     -p 15672:15672 \ 
     rabbitmq

Once our container is up and running you should be able to browse to the RabbitMQ Admin interface at http://hostname_ip:15672 and login with the credentials guest:guest.

We'll need to create an exchange and queue to route the logging data.

Deploy Graylog

Next, we'll deploy the Graylog all-in-one image that contains preconfigured Elasticsearch and MongoDB servers along with the Graylog server and a web console. We'll link in the RabbitMQ container that we started previously, expose Graylog's web component on port 9000, and finally specify an admin username and password.

$> docker run -d --name graylog \ 
     --link rabbitmq:rabbitmq \ 
     -p 9000:9000 \ 
     -e GRAYLOG_USERNAME=admin \ 
     -e GRAYLOG_PASSWORD=somepasswordatleast16chars \ 
     graylog/allinone

See the docker README for more container settings and options.

At this point, you should have a running Graylog stack but no way to feed in log data, so we'll now attach a GELF AMQP input.

Log into the Graylog web console http://hostname_ip:9000 with the username and password you provided when launching the docker container.

Once logged in, select "System / Inputs -> Inputs" from the top menu and then select "GELF AMQP" and click "Launch new input".

Leave the default settings on all fields except the following...

  • Title: GELF AMQP or anything you want
  • Broker hostname: rabbitmq the alias for the docker link when launching the Graylog container
  • Queue: log-messages
  • Allow throttling this input: true, check this feature

Configure Application Loggers

There are many ways to send data to Graylog but for this example, I'm just going to focus on Java logging... particularly Log4j.

Simply add a log4j GELF appender library (t0xa/gelfj) to your project:

<dependencies> 
    ... 
    <dependency> 
        <groupId>org.graylog2</groupId> 
        <artifactId>gelfj</artifactId> 
        <version>1.1.10</version> 
        <scope>compile</scope> 
    </dependency> 
    ... 
</dependencies>

Then configure a GELF appender to your log4j.properties:

# Define the graylog2 destination 
log4j.appender.graylog2=org.graylog2.log.GelfAppender 
log4j.appender.graylog2.amqpURI=amqp://hostname_ip:5672 
log4j.appender.graylog2.amqpExchangeName=log-messages 
log4j.appender.graylog2.amqpRoutingKey=log-messages 
log4j.appender.graylog2.amqpMaxRetries=5 
log4j.appender.graylog2.facility=test-application 
log4j.appender.graylog2.layout=org.apache.log4j.PatternLayout 
log4j.appender.graylog2.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%t] [%c{1}] - %m%n 
log4j.appender.graylog2.additionalFields={'environment': 'DEV', 'application': 'Sample Java App'} 
log4j.appender.graylog2.extractStacktrace=true 
log4j.appender.graylog2.addExtendedInformation=true 
# Send all INFO logs to graylog2 
log4j.rootLogger=INFO, graylog2

Or in a Grails Config.groovy:

log4j.main = { 
    appenders { 
        appender new org.graylog2.log.GelfAppender(
                name: 'graylog', 
                amqpURI: 'amqp://hostname_ip:5672', 
                amqpExchangeName: "log-messages", 
                amqpRoutingKey: "log-messages", 
                amqpMaxRetries: 5, 
                facility: 'test-application', 
                layout: pattern(conversionPattern: "%d{HH:mm:ss,SSS} %-5p [%t] [%c{1}] - %m%n"), 
                additionalFields: "{'applicationEnvironment': '${environment}', 'applicationName': '${applicationName}', 'applicationVersion': '${applicationVersion}'}", 
                extractStacktrace: true, 
                addExtendedInformation: true 
        ) 
    } 
    root { info 'graylog' } 
}

If you're not using Java, there are many other GELF appenders for other languages.

Java

PHP = gelf-php