предисловие
Журналы всегда сопровождали наши процессы разработки и эксплуатации. Когда в системе есть ошибка, она часто подключается к серверу через Xshell, находит файл журнала и постепенно исследует источник проблемы.
С быстрым развитием Интернета наши системы становятся все больше и больше. Опора на визуальный анализ файлов журналов для устранения неполадок вышла на первый план:
- В распределенной кластерной среде количество серверов может достигать сотен и тысяч, как их точно разместить?
- Как в микросервисной архитектуре найти контекстную информацию других сервисов на основе информации об исключениях?
- Поскольку файл журнала продолжает расти, он может столкнуться с тем, что его нельзя открыть непосредственно на сервере.
- Текстовый поиск работает слишком медленно, не может выполнять запросы в нескольких измерениях и т. д.
Столкнувшись с этими проблемами, нам необходимо централизованное управление журналами для унифицированного сбора, управления и доступа к журналам на всех узлах сервера.
И сегодня наши средства - использоватьElastic Stack
чтобы решить их.
1. Что такое эластичный стек?
Некоторые люди могут чувствовать себя немного незнакомыми с Elastic, поскольку его предшественником является ELK, а Elastic Stack является заменой продукта ELK Stack.
Elastic Stack соответствует четырем проектам с открытым исходным кодом.
- Beats
Платформа Beats представляет собой набор одноцелевых сборщиков данных, которые собирают различные типы данных. Такие как файлы, системный мониторинг, журнал событий Windows и т. д.
- Logstash
Logstash — это конвейер обработки данных на стороне сервера, способный одновременно принимать и преобразовывать данные из нескольких источников. Да, он может как собирать данные, так и преобразовывать их. Неструктурированные данные собираются и форматируются в удобные типы с помощью фильтров.
- Elasticsearch
Elasticsearch — это распределенная система поиска и аналитики на основе JSON. Как ядро Elastic Stack, он отвечает за централизованное хранение данных. Мы использовали Beats для сбора данных выше, и после преобразования через Logstash их можно сохранить в Elasticsearch.
- Kibana
Наконец, вы можете визуализировать данные в своем собственном Elasticsearch через Kibana.
Примеры в этой статьеSpringBoot+Dubbo
микросервисная архитектура в сочетании сElastic Stack
интегрировать журналы. Архитектура выглядит следующим образом:
注意,阅读本文需要了解ELK组件的基本概念和安装。本文不涉及安装和基本配置过程,重点是如何与项目集成,达成上面的需求。
2. Сбор и преобразование
1. ФайлБит
В проекте SpringBoot мы сначала настраиваем Logback, чтобы определить расположение файла журнала.
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.dir}/logs/order.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${user.dir}/logs/order.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder>
<pattern></pattern>
</encoder>
</appender>
Filebeat
Предоставляет упрощенный метод пересылки и агрегирования журналов и файлов.
Итак, нам нужно рассказатьFileBeat
Расположение файлов журнала и куда пересылать содержимое.
Как показано ниже, мы настроилиFileBeat
читатьusr/local/logs
Все файлы журнала по пути.
- type: log
# Change to true to enable this input configuration.
enabled: true
# Paths that should be crawled and fetched. Glob based paths.
paths:
- /usr/local/logs/*.log
Тогда расскажиFileBeat
Направить собранные данные вLogstash
.
#----------------------------- Logstash output --------------------------------
output.logstash:
# The Logstash hosts
hosts: ["192.168.159.128:5044"]
Кроме того,FileBeat
При сборе файловых данных они считываются построчно. ноFileBeat
Собранные файлы могут содержать сообщения, состоящие из нескольких строк текста.
Например, преднамеренные новые строки в фреймворках с открытым исходным кодом:
2019-10-29 20:36:04.427 INFO org.apache.dubbo.spring.boot.context.event.WelcomeLogoApplicationListener
:: Dubbo Spring Boot (v2.7.1) : https://github.com/apache/incubator-dubbo-spring-boot-project
:: Dubbo (v2.7.1) : https://github.com/apache/incubator-dubbo
:: Discuss group : dev@dubbo.apache.org
Или информация о стеке исключений Java:
2019-10-29 21:30:59.849 INFO com.viewscenes.order.controller.OrderController http-nio-8011-exec-2 开始获取数组内容...
java.lang.IndexOutOfBoundsException: Index: 3, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:657)
at java.util.ArrayList.get(ArrayList.java:433)
Итак, нам также необходимо настроитьmultiline
, чтобы указать, какие строки являются частью одного события.
multiline.pattern
Указывает шаблон регулярного выражения для сопоставления.
multiline.negate
Определяет, является ли это отрицательным режимом.
multiline.match
Как сгруппировать совпадающие строки в событие, установленное после или до.
Возможно, это прозвучит грубо, давайте рассмотрим набор конфигураций:
# The regexp Pattern that has to be matched. The example pattern matches all lines starting with [
multiline.pattern: '^\<|^[[:space:]]|^[[:space:]]+(at|\.{3})\b|^java.'
# Defines if the pattern set under pattern should be negated or not. Default is false.
multiline.negate: false
# Match can be set to "after" or "before". It is used to define if lines should be append to a pattern
# that was (not) matched before or after or as long as a pattern is not matched based on negate.
# Note: After is the equivalent to previous and before is the equivalent to to next in Logstash
multiline.match: after
В приведенном выше файле конфигурации сказано, что если текстовое содержимое< 或 空格 或空格+at+包路径 或 java.
начинается, то рассматривайте эту строку как продолжение предыдущей строки, а не как новую строку.
Приведенная выше информация о стеке исключений Java соответствует этой закономерности. так,FileBeat
будет
java.lang.IndexOutOfBoundsException: Index: 3, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:657)
at java.util.ArrayList.get(ArrayList.java:433)
это содержание как开始获取数组内容...
часть.
2. Логсташ
существуетLogback
Когда мы печатаем журнал, мы обычно приносим уровень журнала, путь к классам выполнения, имя потока и другую информацию.
Важным сообщением является то, что мы находимся вELK
При просмотре журналов вы хотите разделить вышеуказанные условия для статистики или точного запроса?
Если да, то вам нужно использоватьLogstash
Фильтр, который анализирует отдельные события, идентифицирует именованные поля для построения структур и преобразовывает их в общий формат.
Тогда в это время мы должны сначала посмотреть, в каком формате мы настраиваем вывод журнала в проекте.
Например, наиболее знакомый нам формат JSON. Первый взглядLogback
Конфигурация:
<pattern>
{"log_time":"%d{yyyy-MM-dd HH:mm:ss.SSS}","level":"%level","logger":"%logger","thread":"%thread","msg":"%m"}
</pattern>
Верно,Logstash
В фильтре также есть плагин для разбора JSON. Мы можем настроить это так:
input{
stdin{}
}
filter{
json {
source => "message"
}
}
output {
stdout {}
}
Такая конфигурация означает, что данные форматируются с помощью парсера JSON. Вводим эту строку:
{
"log_time":"2019-10-29 21:45:12.821",
"level":"INFO",
"logger":"com.viewscenes.order.controller.OrderController",
"thread":"http-nio-8011-exec-1",
"msg":"接收到订单数据."
}
Logstash
вернет отформатированное содержимое:
Но синтаксический анализатор JSON не очень подходит, потому что поле msg в журнале, который мы печатаем, может само по себе быть форматом данных JSON.
Например:
{
"log_time":"2019-10-29 21:57:38.008",
"level":"INFO",
"logger":"com.viewscenes.order.controller.OrderController",
"thread":"http-nio-8011-exec-1",
"msg":"接收到订单数据.{"amount":1000.0,"commodityCode":"MK66923","count":5,"id":1,"orderNo":"1001"}"
}
В это время анализатор JSON сообщит об ошибке. тогда что нам делать?
Logstash
Имея богатую библиотеку подключаемых модулей фильтров или если вы уверены в регулярных выражениях, вы также можете написать соответствующие выражения.
как и мыLogback
В соответствии с настройками был определен наш формат содержимого журнала, будь то формат JSON или другие форматы.
Поэтому автор сегодня рекомендует еще один: Dissect.
Dissect filter — это операция разделения. В отличие от обычной операции разделения, которая применяет один разделитель ко всей строке, эта операция применяет набор разделителей к строковому значению. Dissect не использует регулярные выражения и работает очень быстро.
Вот, например, автор|
как разделитель.
input{
stdin{}
}
filter{
dissect {
mapping => {
"message" => "%{log_time}|%{level}|%{logger}|%{thread}|%{msg}"
}
}
}
output {
stdout {}
}
затем вLogback
Чтобы настроить формат журнала таким образом:
<pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS}|%level|%logger|%thread|%m%n
</pattern>
Наконец, правильный результат также может быть получен:
На данный момент завершены сбор данных и преобразование формата. Конечно, все приведенные выше конфигурации являются консольным вводом и выводом.
Давайте рассмотрим серьезную конфигурацию, которая начинается сFileBeat
данные собираются вdissect
Преобразуйте формат и выведите данные вelasticsearch
.
input {
beats {
port => 5044
}
}
filter{
dissect {
mapping => {
"message" => "%{log_time}|%{level}|%{logger}|%{thread}|%{msg}"
}
}
date{
match => ["log_time", "yyyy-MM-dd HH:mm:ss.SSS"]
target => "@timestamp"
}
}
output {
elasticsearch {
hosts => ["192.168.216.128:9200"]
index => "logs-%{+YYYY.MM.dd}"
}
}
Если ничего другого, откройте браузер, и мы сможем просмотреть журнал в Kibana. Например, мы рассматриваем уровень журнала какDEBUG
запись:
3. Отслеживание
Представьте, что мы отправляем запрос на заказ на фронтенде. Если серверная система представляет собой микросервисную архитектуру, она может использовать несколько сервисов, таких как система инвентаризации, система купонов, система учетных записей, система заказов и т. д. Как отследить цепочку вызовов этого запроса?
1. Механизм МДК
Во-первых, нам нужно понять механизм MDC.
MDC — сопоставленные диагностические контексты, которые по сути являются сопоставлениями, поддерживаемыми структурой ведения журналов. где код приложения предоставляет пары ключ-значение, которые затем могут быть вставлены в сообщения журнала платформой ведения журнала.
Короче говоря, мы использовалиMDC.PUT(key,value)
,ТакLogback
Это значение может быть автоматически распечатано в журнале.
существуетSpringBoot
, мы можем сначала написатьHandlerInterceptor
, перехватывать все запросы для созданияtraceId
.
@Component
public class TraceIdInterceptor implements HandlerInterceptor {
Snowflake snowflake = new Snowflake(1,0);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
MDC.put("traceId",snowflake.nextIdStr());
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView){
MDC.remove("traceId");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex){}
}
затем вLogback
настроить его, пусть этоtraceId
появляется в сообщениях журнала.
<pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS}|%level|%logger|%thread|%X{traceId}|%m%n
</pattern>
2. Фильтр Даббо
Другой вопрос, как заставить это работать в микросервисной архитектуре.traceId
Пройти вперед и назад.
Знаком сDubbo
Друзья, можете подумать о неявных параметрах. Да, мы просто используем это, чтобы сделать это.traceId
коробка передач.
@Activate(group = {Constants.PROVIDER, Constants.CONSUMER}, order = 99)
public class TraceIdFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
String tid = MDC.get("traceId");
String rpcTid = RpcContext.getContext().getAttachment("traceId");
boolean bind = false;
if (tid != null) {
RpcContext.getContext().setAttachment("traceId", tid);
} else {
if (rpcTid != null) {
MDC.put("traceId",rpcTid);
bind = true;
}
}
try{
return invoker.invoke(invocation);
}finally {
if (bind){
MDC.remove("traceId");
}
}
}
}
После написания этого мы можем с радостью просмотреть всю информацию журнала запроса. Например, следующий запрос, журналы двух систем обслуживания заказов и службы инвентаризации.
4. Резюме
В этой статье описываетсяElastic Stack
основная концепция. и черезSpringBoot+Dubbo
Проект демонстрирует, как добиться централизованного управления и отслеживания журналов.
По факту,Kibana
С дополнительным анализом и статистическими функциями. Так что его роль не ограничивается логированием.
Кроме тогоElastic Stack
Производительность тоже очень хорошая. Автор записал на виртуальную машину 1 млн+ пользовательских данных, размер индекса 1.1G, скорость запросов и статистики не уступает.