Однажды я услышал о стеке ELK и о его преимуществах, поэтому решил взять его в свои руки. К сожалению, я изо всех сил пытался найти надежную документацию и дополнительный контент для начала работы. Итак, я решил написать свой.
Для начала давайте познакомимся с тем, что такое стек ELK:
«ELK» является аббревиатурой трех открытых проектов: Elasticsearch, Logstash и Kibana. Elasticsearch — это поисковая и аналитическая система. Logstash — это конвейер обработки данных на стороне сервера, который получает данные из нескольких источников одновременно, преобразует их и затем отправляет в «хранилище», подобное Elasticsearch. Kibana позволяет пользователям визуализировать данные с помощью диаграмм и графиков в Elasticsearch. https://www.elastic.co/what-is/elk-stack
В общем,
- E lasticsearch заботится о хранилище и управляет поиском и аналитикой через конечные точки REST.
- L ogstash — это «pac-man», который поглощает, фильтрует и отправляет данные.
- К ибана отвечает за причудливый способ просмотра результатов.
Для начала нам нужно скачать стек (я на Windows):
- Elasticsearch — https://www.elastic.co/downloads/elasticsearch ,
- Logstash — https://www.elastic.co/downloads/logstash (zip-архив).
- Кибана — https://www.elastic.co/downloads/kibana .
После загрузки вы можете разархивировать их сразу. Мы приедем к ним позже. Теперь мы собираемся настроить простой проект Spring Boot, который будет генерировать для некоторых журналов, которые мы рассмотрим позже. Вот зависимости, которые нам понадобятся на данный момент:
XML
1
<dependency>
2
<groupId>org.springframework.boot</groupId>
3
<artifactId>spring-boot-starter-actuator</artifactId>
4
</dependency>
5
<dependency>
7
<groupId>org.projectlombok</groupId>
8
<artifactId>lombok</artifactId>
9
<optional>true</optional>
10
</dependency>
Как бы просто это ни было, я решил использовать Actuator, поскольку это будет быстрая настройка для получения некоторых журналов. Теперь давайте создадим сервис с именем, ActuatorMetricsService
который будет отвечать за наши журналы.
Джава
xxxxxxxxxx
1
2
3
public class ActuatorMetricsService {
4
private final MetricsEndpoint metricsEndpoint;
6
private final HealthEndpoint healthEndpoint;
7
private final InfoEndpoint infoEndpoint;
8
10
public ActuatorMetricsService(MetricsEndpoint metricsEndpoint, HealthEndpoint healthEndpoint, InfoEndpoint infoEndpoint) {
11
this.metricsEndpoint = metricsEndpoint;
12
this.healthEndpoint = healthEndpoint;
13
this.infoEndpoint = infoEndpoint;
14
}
15
initialDelay = 6000, fixedDelay = 60000) (
17
public void fetchMetrics() {
18
metricsEndpoint.listNames().getNames().forEach(n -> {
19
log.info(n + " = " + metricsEndpoint.metric(n, Collections.emptyList()).getMeasurements());
20
});
21
}
22
initialDelay = 6000, fixedDelay = 30000) (
24
public void fetchHealth() {
25
HealthComponent health = healthEndpoint.health();
26
log.info("health = {}" , health.getStatus());
27
}
28
initialDelay = 6000, fixedDelay = 60000) (
30
public void fetchInfo() {
31
infoEndpoint.info().forEach((k, v) -> log.info(k + " = " + v));
32
}
33
}
Об этом классе следует упомянуть несколько вещей, если вы не знакомы с тем, что видите:
@Slf4j
- Создаетprivate static final org.slf4j.Logger log =
org.slf4j.LoggerFactory.getLogger(LogExample.class)
для использования позже в нашем коде.MetricsEndpoint
,HealthEndpoint
,InfoEndpoint
- классы от привода используются , чтобы выставить информацию о показателях, здоровье приложений и прикладной информации.@Scheduled
- Аннотация, которая отмечает метод, который будет запланирован. Чтобы это работало, вам нужно включить планирование с помощью@EnableScheduling
.
Мы будем получать метрики и информацию о приложениях каждую минуту с начальной задержкой в шесть секунд и о состоянии приложения каждые 30 секунд с начальной задержкой в шесть секунд.
Теперь давайте приступим к настройке наших логов. Для этого нам нужно создать logback.xml
каталог ресурсов.
XML
xxxxxxxxxx
1
2
<configuration>
3
<property scope="context" name="log.fileExtension" value="log"></property>
4
<property scope="context" name="log.directory" value="/logs"></property>
5
<property scope="context" name="log.fileName" value="bootiful-elk"></property>
6
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
8
<layout class="ch.qos.logback.classic.PatternLayout">
9
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [${HOSTNAME}] [%thread] %level %logger{36}@%method:%line - %msg%n</pattern>
10
</layout>
11
</appender>
12
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
14
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
15
<fileNamePattern>${log.directory}/${log.fileName}.%d{yyyy-MM-dd}.${log.fileExtension}</fileNamePattern>
16
</rollingPolicy>
17
<encoder>
18
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [${HOSTNAME}] [%thread] %level %logger{36}@%method:%line - %msg%n</pattern>
19
</encoder>
20
</appender>
21
<root level="INFO">
23
<appender-ref ref="STDOUT"></appender>
24
<appender-ref ref="FILE"></appender>
25
</root>
26
</configuration>
Это даст нам логи как в нашей консоли, так и в файле. Запуск приложения теперь должен давать такой вид вывода, конечно, с разными / одинаковыми значениями:
[2020-03-22 20: 28: 25.256] [LT-IPASCARI] [планирование-1] INFO celbsActuatorMetricsService @ fetchHealth: 39 - health = UP
[2020-03-22 20: 28: 26.262] [LT-IPASCARI] [scheduling-1] INFO celbsActuatorMetricsService @ lambda $ fetchMetrics $ 0: 32 - jvm.memory.max = [MeasurementSample {statistics = VALUE, значение = 5.577900031E9} ]
...
[2020-03-22 20: 28: 26.716] [LT-IPASCARI] [scheduling-1] INFO celbsActuatorMetricsService @ lambda $ fetchMetrics $ 0: 32 - process.start.time = [MeasurementSample {statistics = VALUE, значение = 1,584901694856E9} ]
[2020-03-22 20: 28: 26.719] [LT-IPASCARI] [scheduling-1] INFO celbsActuatorMetricsService @ lambda $ fetchInfo $ 1: 44 - app = {name = bootiful-elk, description = Демонстрационный проект для Spring Boot, версия = 0.0.1-SNAPSHOT, кодировка = UTF-8, java = {версия = 1.8.0_171}}
Же журналы должны быть в файле тоже. Теперь, когда мы настроили наше приложение, пришло время добраться до ELK . Я собираюсь обсудить два метода получения ваших журналов в Кибане :
- Скажите Logstash, чтобы просмотреть ваши файлы журнала.
- Скажите Logstash прослушать записи в журнале.
Начнем с первого, так как с моей точки зрения его сложнее всего настроить.
Прежде всего, вы должны знать, что есть три важных части Logstash:
- Плагин ввода - позволяет Logstash читать определенный источник событий. Вы можете проверить все из них здесь (https://www.elastic.co/guide/en/logstash/current/input-plugins.html ), но мы собираемся с
file
этим для демонстрации. - Плагин фильтра - выполняет промежуточную обработку события. Фильтры часто применяются условно, в зависимости от характеристик события. Опять же , здесь, https://www.elastic.co/guide/en/logstash/current/filter-plugins.html , вы можете найти их много, но мы будем использовать некоторые из них, такие как
grok
,date
,mutate
- Выходной плагин - отправляет данные о событии в конкретный пункт назначения. Выходы являются заключительным этапом в конвейере событий. Вот все плагины https://www.elastic.co/guide/en/logstash/current/output-plugins.html , но в этой демонстрации мы будем использовать
elasticsearch
иstdout
.
Теперь, когда у нас есть основы, давайте запачкаем руки. Найдите вашу распакованную папку Logstash и перейдите в папку config (logstash-7.6.1 \ config) и создайте файл logstash-file.conf
с таким именем :
Джава
xxxxxxxxxx
1
input {
2
file {
3
path => "D:/logs/*.log"
4
codec => "plain"
5
type => "logback"
6
}
7
}
8
output {
10
stdout { }
11
elasticsearch {
12
hosts => ["localhost:9200"]
13
index => "bootiful-elk-file-%{+YYYY.MM.dd}"
14
}
15
}
Давайте посмотрим на то, что мы должны сделать здесь, поскольку это очень просто на данный момент.
- входной плагин
- файл - это означает, что мы имеем дело с файлом в качестве входных данных
- путь - это абсолютный путь к вашим файлам журналов. Это обязательная настройка.
- кодек - удобный метод для декодирования ваших данных перед их вводом, без необходимости отдельного фильтра в вашем конвейере Logstash. По умолчанию это просто, но я все еще использовал это, так как это важно.
- Тип - хранится как часть самого события, поэтому вы также можете использовать его для поиска в Кибане.
- файл - это означает, что мы имеем дело с файлом в качестве входных данных
- выходной плагин
- stdout - простой вывод, который выводится на STDOUT оболочки, в которой работает Logstash. Этот вывод может быть очень удобным при отладке конфигураций плагинов. Кодек по умолчанию - rubydebug: выводит данные о событиях с использованием библиотеки ruby "awesome_print".
- asticsearch - если вы планируете использовать веб-интерфейс Kibana, используйте выходной плагин Elasticsearch, чтобы получить данные журнала в Elasticsearch.
- hosts - устанавливает хост (ы) удаленного экземпляра. В этом случае, когда мы запустим Elasticsearch локально, он будет включен
127.0.0.1:9200
. - index - Индекс для записи событий. Это может быть динамически с использованием синтаксиса% {foo}. Значение по умолчанию, равное "
logstash-%{+YYYY.MM.dd}
", разделит ваши индексы по дням, чтобы вы могли легче удалять старые данные или выполнять поиск только в определенных диапазонах дат.
- hosts - устанавливает хост (ы) удаленного экземпляра. В этом случае, когда мы запустим Elasticsearch локально, он будет включен
Хорошо, теперь, когда мы понимаем, что имеем в logstash-file.conf, пришло время запустить наши сервисы и просмотреть их. Мы начнем в следующем порядке:
- Elasticsearch --asticsearch-7.6.1 \ bin \asticsearch.bat . Вы можете проверить, если началось с
curl -G 127.0.0.1:9200
. - Kibana - kibana-7.6.1-windows-x86_64 \ bin \ kibana.bat . Запустится веб-интерфейс Kibana
127.0.0.1:5601
. Вы можете пойти дальше и проверить это (пропустить все уроки). - Logstash - logstash-7.6.1 \ bin и из командной строки выполните следующую команду, чтобы подобрать созданную конфигурацию
logstash -f ../config/logstash-file.conf
. - Приложение - запустить приложение Spring.
Мы настроили потоковую передачу логов, но мы хотим проверить их, верно? Чтобы сделать это, мы должны связать наш Elasticsearch индекс Kibana . Для этого перейдите в веб-интерфейс Kibana, проверьте шаблоны индексов и нажмите « Создать параметр индекса» . Оттуда вы увидите наш определенный шаблон, bootiful-elk-file-2020.03.22
наш определенный шаблон, напишите его в разделе шаблона индекса и нажмите «Далее», затем вы можете добавить некоторые настройки, а сейчас нажмите « Я не хочу использовать фильтр времени» и завершите настройку. С этого момента вы можете перейти к Discover и проверить ваши журналы.
Вы должны увидеть что-то подобное. Если нет, поиграйтесь с доступными полями фильтра. Но вы можете видеть, что что-то не так, мы получили целую строку журнала в качестве сообщения, что нам не очень полезно, так как мы не можем использовать другие поля из этой строки журнала. Для этого нам нужно включить плагин фильтра, который будет разбивать нашу строку журнала на отдельные поля, которые можно использовать как поля фильтра, поля сортировки и в KQL (Kibana Query Language) .
Давайте изменим нашу конфигурацию, чтобы она выглядела так
Джава
xxxxxxxxxx
1
input {
2
file {
3
path => "D:/logs/*.log"
4
codec => "plain"
5
type => "logback"
6
}
7
}
8
filter {
10
if [message] =~ "\tat" {
11
grok {
12
match => ["message", "^(\tat)"]
13
add_tag => ["stacktrace"]
14
}
15
}
16
grok {
18
match => [ "message",
19
"(?m)\[%{TIMESTAMP_ISO8601:timestamp}\] \[%{HOSTNAME:host}\] \[%{DATA:thread}\] %{LOGLEVEL:logLevel} %{DATA:class}@%{DATA:method}:%{DATA:line} \- %{GREEDYDATA:msg}"
20
]
21
}
22
date {
24
match => [ "timestamp" , "yyyy-MM-dd HH:mm:ss.SSS" ]
25
}
26
mutate {
28
remove_field => ["message"]
29
}
30
}
31
output {
33
stdout { codec => rubydebug }
34
elasticsearch {
35
hosts => ["localhost:9200"]
36
index => "bootiful-elk-file-%{+YYYY.MM.dd}"
37
}
38
}
Теперь давайте поговорим о том, что нового здесь
- плагин
фильтра- Если конструкция - мы проверяем, что если наше сообщение содержит вкладку и мы будем квалифицировать его как трассировку стека, поэтому мы добавим соответствующий тег.
- grok - Grok - отличный способ разбить неструктурированные данные журнала на что-то структурированное и запрашиваемое. Этот инструмент идеально подходит для журналов системного журнала, apache и других журналов веб-сервера, журналов mysql и вообще для любого формата журнала, который обычно написан для людей, а не для использования на компьютере. Убедитесь, что ваш шаблон совпадения соответствует шаблону вашего журнала, вы можете проверить его с помощью онлайн-валидаторов.
- date - используется для анализа дат из полей, а затем с использованием этой даты или метки времени в качестве метки времени logstash для события.
- mutate - позволяет выполнять общие мутации на полях. Вы можете переименовывать, удалять, заменять и изменять поля в ваших событиях. Здесь я хотел удалить поля «сообщения», так как разбил его на кусочки с помощью Grok, поэтому я не хотел, чтобы он просто лежал вокруг.
Хорошо, давайте перезапустим наш Logstash - logstash-7.6.1\bin
и из командной строки запустите следующую команду, чтобы забрать созданную конфигурацию logstash -f ../config/logstash-file.conf
и посмотреть, что у нас сейчас в Кибане, вы должны увидеть что-то вроде этого.
Теперь мы получили правильный вывод, вы можете заметить, что наша длинная строка сообщений разбита на множество отдельных полей, которые теперь можно использовать для эффективного поиска и анализа. Это почти то же самое, что и в отношении первого метода: вы можете пойти дальше и исследовать дополнительные параметры фильтрации, способы настройки данных, опробовать различные кодеки для конкретных плагинов и т. Д.
О втором способе, о передаче строк журнала непосредственно из приложения Spring Boot в Logstash -> Elasticsearch -> Kibana . Для этого нам нужно добавить еще одну зависимость к нашему, pom.xml
чтобы она выглядела следующим образом
XML
xxxxxxxxxx
1
<dependency>
2
<groupId>org.springframework.boot</groupId>
3
<artifactId>spring-boot-starter-actuator</artifactId>
4
</dependency>
5
<dependency>
7
<groupId>net.logstash.logback</groupId>
8
<artifactId>logstash-logback-encoder</artifactId>
9
<version>6.3</version>
10
</dependency>
12
<dependency>
14
<groupId>org.projectlombok</groupId>
15
<artifactId>lombok</artifactId>
16
<optional>true</optional>
17
</dependency>
Теперь, когда мы получили нашу зависимость, мы можем пойти дальше и изменить нашу logback.xml
тоже.
XML
xxxxxxxxxx
1
2
<configuration>
3
<property scope="context" name="log.fileExtension" value="log"/>
4
<property scope="context" name="log.directory" value="/logs"/>
5
<property scope="context" name="log.fileName" value="bootiful-elk"/>
6
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
8
<layout class="ch.qos.logback.classic.PatternLayout">
9
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [${HOSTNAME}] [%thread] %level %logger{36}@%method:%line - %msg%n</pattern>
10
</layout>
11
</appender>
12
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
14
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
15
<fileNamePattern>${log.directory}/${log.fileName}.%d{yyyy-MM-dd}.${log.fileExtension}</fileNamePattern>
16
</rollingPolicy>
17
<encoder>
18
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [${HOSTNAME}] [%thread] %level %logger{36}@%method:%line - %msg%n</pattern>
19
</encoder>
20
</appender>
21
<appender name="STASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
23
<destination>127.0.0.1:5000</destination>
24
<!-- encoder is required -->
25
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
26
<keepAliveDuration>5 minutes</keepAliveDuration>
27
</appender>
28
<root level="INFO">
30
<appender-ref ref="STDOUT"/>
31
<appender-ref ref="STASH"/>
32
<appender-ref ref="FILE"/>
33
</root>
34
</configuration>
35
Вы можете заметить, что изменилось: появился новый appender logback.xml
, то есть LogstashTcpSocketAppender
и LogstashEncoder
, который будет кодировать в JSON наши строки журнала и отправлять их через TCP в 127.0.0.1:5000
.
Теперь, когда мы знаем формат наших строк журнала и их назначение, мы должны установить пункт назначения, который будет прослушивать события JSON 127.0.0.1:5000
. Для этого мы создадим новый файл в папке конфигурации Logstash (logstash-7.6.1 \ config) и создадим файл logstash-tcp.conf
с таким содержимым:
Простой текст
xxxxxxxxxx
1
input {
2
tcp {
3
port => "5000"
4
codec => json_lines
5
}
6
}
7
output {
9
stdout {}
10
elasticsearch {
11
hosts => ["http://localhost:9200"]
12
index => "bootiful-elk-tcp-%{+YYYY.MM.dd}"
13
}
14
}
Как уже говорилось, я настроил пункт назначения, который, по сути, является источником / вводом Logstash, плагином ввода tcp с портом 5000
и кодеком json_lines
. Результат, как вы могли заметить, тот же, с одним важным изменением. Я не хотел заполнять контейнер файловых журналов, созданный ранее этими строками журналов, поэтому я решил использовать другой индекс bootiful-elk-tcp-%{+YYYY.MM.dd}
. Кроме того, я не использую здесь какой-либо плагин фильтра, поскольку сообщение кодируется / декодируется из JSON, который уже разбивает строку журнала на части.
Хорошо, теперь мы можем перезапустить наш Logstash - logstash-7.6.1\bin
и из командной строки выполнить следующую команду, чтобы забрать созданную конфигурацию logstash -f ../config/logstash-tcp.conf
. Перезапустите приложение Spring Boot и посмотрите, что у нас есть в Кибане. (Не забудьте повторить шаги по созданию нового шаблона индекса.) Вы должны увидеть что-то вроде этого:
Обратите внимание, что мы получили немного другие поля, но близкие к тем, которые мы ранее отображали с помощью Grok.
Хорошо, в основном это так. Вы можете найти эти 2 .conf-файла в папке ресурсов моего проекта и другие файлы, относящиеся к этой статье, здесь https://github.com/theFaustus/bootiful-elk .