今天的帖子的主题与日常的编码和开发无关,但是涵盖了一个非常重要的主题:我们的应用程序日志文件。 我们的应用程序确实会生成大量日志,如果处理正确,则非常有助于解决问题。 如果您启动并运行一个应用程序并没什么大不了,但是如今,应用程序(特别是Web应用程序)可以在数百台服务器上运行。 有了这样的规模,找出问题所在就成为了挑战。 拥有某种视图将所有正在运行的应用程序中的所有日志聚合到单个仪表板中,这样我们就可以看到由碎片构成的整体画面,这不是很好吗? 请欢迎: Logstash ,日志聚合框架。
尽管它不是唯一可用的解决方案,但我发现Logstash易于使用且易于集成。 首先,我们甚至不需要在应用程序方面做任何事情, Logstash可以为我们完成所有工作。 让我介绍一个示例项目:具有某些多线程活动的独立Java应用程序。 使用出色的Logback库(配置为SLF4J可以无缝替换)对配置文件进行日志记录。 POM文件看起来非常简单:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelversion>4.0.0</modelversion><groupid>com.example</groupid><artifactid>logstash</artifactid><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><properties><project.build.sourceencoding>UTF-8</project.build.sourceencoding><logback.version>1.0.6</logback.version></properties><dependencies><dependency><groupid>ch.qos.logback</groupid><artifactid>logback-classic</artifactid><version>${logback.version}</version></dependency><dependency><groupid>ch.qos.logback</groupid><artifactid>logback-core</artifactid><version>${logback.version}</version></dependency></dependencies><build><plugins><plugin><groupid>org.apache.maven.plugins</groupid><artifactid>maven-compiler-plugin</artifactid><version>3.0</version><configuration><source>1.7<target>1.7</target></configuration></plugin></plugins></build>
</project>
而且只有一个名为Starter的 Java类,它使用Executors服务同时执行一些工作。 当然,每个线程都会进行一些日志记录,并且有时会引发异常。
package com.example.logstash;import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class Starter {private final static Logger log = LoggerFactory.getLogger( Starter.class );public static void main( String[] args ) {final ExecutorService executor = Executors.newCachedThreadPool();final Collection< Future< Void > > futures = new ArrayList< Future< Void > >();final Random random = new Random();for( int i = 0; i < 10; ++i ) {futures.add( executor.submit(new Callable< Void >() {public Void call() throws Exception {int sleep = Math.abs( random.nextInt( 10000 ) % 10000 );log.warn( 'Sleeping for ' + sleep + 'ms' );Thread.sleep( sleep );return null;}}));}for( final Future< Void > future: futures ) {try {Void result = future.get( 3, TimeUnit.SECONDS );log.info( 'Result ' + result );} catch (InterruptedException | ExecutionException | TimeoutException ex ) {log.error( ex.getMessage(), ex );} }}
}
这个想法不仅要演示简单的单行日志事件,还要演示著名的Java堆栈跟踪。 当每个线程睡眠随机的时间间隔时,每当从底层的Future对象请求计算结果并花费3秒钟以上的时间返回时,都会引发TimeoutException。 最后一部分是Logback配置( logback.xml ):
<configuration scan="true" scanperiod="5 seconds"><appender name="FILE" class="ch.qos.logback.core.FileAppender"><file>/tmp/application.log</file><append>true</append><encoder><pattern>[%level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %logger{36} - %msg%n</pattern></encoder></appender><root level="INFO"><appender-ref ref="FILE"></appender-ref></root>
</configuration>
而且我们很高兴去! 请注意,文件路径/tmp/application.log对应于Windows上的c:\ tmp \ application.log 。 运行我们的应用程序将用以下内容填充日志文件:
[WARN] 2013-02-19 19:26:03.175 [pool-2-thread-1] com.example.logstash.Starter - Sleeping for 2506ms
[WARN] 2013-02-19 19:26:03.175 [pool-2-thread-4] com.example.logstash.Starter - Sleeping for 9147ms
[WARN] 2013-02-19 19:26:03.175 [pool-2-thread-9] com.example.logstash.Starter - Sleeping for 3124ms
[WARN] 2013-02-19 19:26:03.175 [pool-2-thread-3] com.example.logstash.Starter - Sleeping for 6239ms
[WARN] 2013-02-19 19:26:03.175 [pool-2-thread-5] com.example.logstash.Starter - Sleeping for 4534ms
[WARN] 2013-02-19 19:26:03.175 [pool-2-thread-10] com.example.logstash.Starter - Sleeping for 1167ms
[WARN] 2013-02-19 19:26:03.175 [pool-2-thread-7] com.example.logstash.Starter - Sleeping for 7228ms
[WARN] 2013-02-19 19:26:03.175 [pool-2-thread-6] com.example.logstash.Starter - Sleeping for 1587ms
[WARN] 2013-02-19 19:26:03.175 [pool-2-thread-8] com.example.logstash.Starter - Sleeping for 9457ms
[WARN] 2013-02-19 19:26:03.176 [pool-2-thread-2] com.example.logstash.Starter - Sleeping for 1584ms
[INFO] 2013-02-19 19:26:05.687 [main] com.example.logstash.Starter - Result null
[INFO] 2013-02-19 19:26:05.687 [main] com.example.logstash.Starter - Result null
[ERROR] 2013-02-19 19:26:08.695 [main] com.example.logstash.Starter - null
java.util.concurrent.TimeoutException: nullat java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:258) ~[na:1.7.0_13]at java.util.concurrent.FutureTask.get(FutureTask.java:119) ~[na:1.7.0_13]at com.example.logstash.Starter.main(Starter.java:43) ~[classes/:na]
[ERROR] 2013-02-19 19:26:11.696 [main] com.example.logstash.Starter - null
java.util.concurrent.TimeoutException: nullat java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:258) ~[na:1.7.0_13]at java.util.concurrent.FutureTask.get(FutureTask.java:119) ~[na:1.7.0_13]at com.example.logstash.Starter.main(Starter.java:43) ~[classes/:na]
[INFO] 2013-02-19 19:26:11.696 [main] com.example.logstash.Starter - Result null
[INFO] 2013-02-19 19:26:11.696 [main] com.example.logstash.Starter - Result null
[INFO] 2013-02-19 19:26:11.697 [main] com.example.logstash.Starter - Result null
[INFO] 2013-02-19 19:26:12.639 [main] com.example.logstash.Starter - Result null
[INFO] 2013-02-19 19:26:12.639 [main] com.example.logstash.Starter - Result null
[INFO] 2013-02-19 19:26:12.639 [main] com.example.logstash.Starter - Result null
现在,让我们看看Logstash可以为我们做些什么。 从下载部分,我们获得了单个JAR文件: logstash-1.1.9-monolithic.jar 。 这就是我们现在所需要的。 不幸的是,由于Windows上的这个错误,我们必须将logstash-1.1.9-monolithic.jar扩展到festash-1.1.9-monolithic文件夹中。 Logstash刚刚三个概念: 输入 滤波器和输出 。 这些在文档中有很好的解释。 在我们的例子中,输入是应用程序的日志文件c:\ tmp \ application.log 。 但是输出是什么? ElasticSearch似乎是一个很好的选择:让我们的日志可以随时索引和搜索。 让我们下载并运行它:
elasticsearch.bat -Des.index.store.type=memory -Des.network.host=localhost
现在我们准备好集成Logstash了 ,它应该拖尾我们的日志文件,并将其直接提供给ElasticSearch 。 以下配置完全可以做到这一点( logstash.conf ):
input {file {add_field => [ 'host', 'my-dev-host' ]path => 'c:\tmp\application.log'type => 'app'format => 'plain'}
}output {elasticsearch_http {host => 'localhost'port => 9200 type => 'app'flush_size => 10}
}filter {multiline {type => 'app'pattern => '^[^\[]'what => 'previous' }
}
乍一看可能看起来不太清楚,但让我解释一下是什么。 因此,输入为c:\ tmp \ application.log ,这是一个纯文本文件( format =>'plain' )。 类型=>'app'用作简单标记,因此可以通过具有相同类型的过滤器将不同类型的输入路由到输出。 add_field => ['host','my-dev-host']允许向传入流fe主机名中注入其他任意数据。
输出非常清晰:通过HTTP的ElasticSearch ,端口9200(默认设置)。 过滤器需要一点魔术,所有这些都是由于Java堆栈跟踪。 多行过滤器会将堆栈跟踪粘贴到其所属的log语句,因此将其存储为单个(大)多行。 让我们运行Logstash :
java -cp logstash-1.1.9-monolithic logstash.runner agent -f logstash.conf
大! 现在,每当我们运行应用程序时, Logstash都会监视日志文件,对其进行过滤,然后将其直接发送到ElasticSearch 。 很酷,但是我们如何进行搜索或至少查看我们拥有什么样的数据? 虽然ElasticSearch有真棒REST API,我们可以用另一种优秀的项目, Kibana ,web用户界面前端为ElasticSearch 。 安装非常简单,无缝。 经过一些必要的步骤后,我们启动了Kibana并开始运行:
ruby kibana.rb
默认情况下, Kibana在端口5601上提供了可用的Web UI,让我们将浏览器指向它,即http:// localhost:5601 / ,我们应该看到类似的内容(请单击图像放大):
我们所有的日志语句(由主机名补充)都在那里。 异常(带有堆栈跟踪)与相关的log语句结合在一起。 日志级别,时间戳,所有内容均已显示。 借助ElasticSearch ,可以立即使用全文搜索。
一切都很棒,但是我们的应用程序非常简单。 这种方法是否可以跨多服务器/多应用程序部署工作? 我很确定它将正常工作。 Logstash与Redis , ZeroMQ , RabbitMQ … 的集成允许捕获来自数十个不同来源的日志并将其合并到一个位置。 非常感谢Logstash伙计们!
参考: 您的日志就是您的数据:来自Andry Redko {devmind}博客的JCG合作伙伴 Andrey Redko的logstash + elasticsearch 。
翻译自: https://www.javacodegeeks.com/2013/02/your-logs-are-your-data-logstash-elasticsearch.html