> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> Как отправлять данные из файла журнала в ClickHouse с помощью Vector

# Интеграция Vector с ClickHouse

export const PartnerBadge = () => {
  return <div className="PartnerBadge">
            <div className="PartnerBadgeIcon">
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                    <polyline points="12.5 9.5 10 12 6 11 2.5 8.5" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1" />
                    <polyline points="4.54 4.41 8 3.5 11.46 4.41" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1" />
                    <path d="M2.15,3.78 L0.55,6.95 A0.5,0.5 0,0,0 0.77,7.62 L2.5,8.5 L4.54,4.41 L2.82,3.55 A0.5,0.5 0,0,0 2.15,3.78 Z" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1" />
                    <path d="M13.5,8.5 L15.23,7.62 A0.5,0.5 0,0,0 15.45,6.95 L13.85,3.78 A0.5,0.5 0,0,0 13.18,3.55 L11.46,4.41 Z" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1" />
                    <path d="M11.5,4.5 L9,4.5 L6.15,7.27 A0.5,0.5 0,0,0 6.24,8.05 C7.33,8.74 8.81,8.72 10,7.5 L12.5,9.5 L13.5,8.5" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1" />
                    <polyline points="7.75 13.5 5.15 12.85 3.5 11.67" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1" />
                </svg>
            </div>
            Партнерская интеграция
        </div>;
};

export const Image = ({img, alt, size = "lg"}) => {
  const normalizedSize = ["sm", "md", "lg"].includes(size) ? size : "lg";
  return <div className={`ch-image-${normalizedSize}`}>
      <Frame>
        <img src={img} alt={alt} />
      </Frame>
    </div>;
};

<PartnerBadge />

Возможность анализировать журналы в реальном времени критически важна для приложений в продакшне.
ClickHouse отлично подходит для хранения и анализа данных журналов благодаря превосходному сжатию (до [170x](https://clickhouse.com/blog/log-compression-170x) для журналов)
и способности быстро агрегировать большие объёмы данных.

В этом руководстве показано, как использовать популярный конвейер данных [Vector](https://vector.dev/docs/introduction/) для отслеживания файла журнала Nginx и отправки данных в ClickHouse.
Приведённые ниже шаги аналогичны для отслеживания любого файла журнала.

**Предварительные требования:**

* У вас уже есть работающий ClickHouse
* У вас установлен Vector

<Steps>
  <Step title="Создайте базу данных и таблицу" id="1-create-a-database-and-table">
    Создайте таблицу для хранения событий журнала:

    1. Начните с создания новой базы данных `nginxdb`:

    ```sql theme={null}
    CREATE DATABASE IF NOT EXISTS nginxdb
    ```

    2. Вставьте всё событие лога как одну строку. Очевидно, это не лучший формат для анализа логов, но ниже мы решим эту проблему с помощью ***materialized views***.

    ```sql theme={null}
    CREATE TABLE IF NOT EXISTS  nginxdb.access_logs (
      message String
    )
    ENGINE = MergeTree()
    ORDER BY tuple()
    ```

    <Note>
      **ORDER BY** задан как **tuple()** (пустой кортеж), так как первичный ключ пока не нужен.
    </Note>
  </Step>

  <Step title="Настройка Nginx" id="2--configure-nginx">
    На этом шаге показано, как настроить ведение журналов в Nginx.

    1. Следующее свойство `access_log` направляет журнал в `/var/log/nginx/my_access.log` в формате **combined**.
       Это значение нужно указать в разделе `http` файла `nginx.conf`:

    ```bash theme={null}
    http {
      include       /etc/nginx/mime.types;
      default_type  application/octet-stream;
      access_log  /var/log/nginx/my_access.log combined;
      sendfile        on;
      keepalive_timeout  65;
      include /etc/nginx/conf.d/*.conf;
    }
    ```

    2. Обязательно перезапустите Nginx, если вам пришлось изменить `nginx.conf`.

    3. Откройте страницы на вашем веб-сервере, чтобы сгенерировать несколько событий в журнале доступа.
       Записи журнала в формате **combined** выглядят следующим образом:

    ```bash theme={null}
    192.168.208.1 - - [12/Oct/2021:03:31:44 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"
    192.168.208.1 - - [12/Oct/2021:03:31:44 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "http://localhost/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"
    192.168.208.1 - - [12/Oct/2021:03:31:49 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"
    ```
  </Step>

  <Step title="Настройка Vector" id="3-configure-vector">
    Vector собирает, преобразует и маршрутизирует журналы, метрики и трассировки (так называемые **источники**) в различные системы назначения (так называемые **приемники**), включая встроенную совместимость с ClickHouse.
    Источники и приемники задаются в файле конфигурации **vector.toml**.

    1. Следующий файл **vector.toml** определяет **источник** типа **file**, который читает новые записи с конца файла **my\_access.log**, а также **приемник** — таблицу **access\_logs**, определенную выше:

    ```bash theme={null}
    [sources.nginx_logs]
    type = "file"
    include = [ "/var/log/nginx/my_access.log" ]
    read_from = "end"

    [sinks.clickhouse]
    type = "clickhouse"
    inputs = ["nginx_logs"]
    endpoint = "http://clickhouse-server:8123"
    database = "nginxdb"
    table = "access_logs"
    skip_unknown_fields = true
    ```

    2. Запустите Vector, используя приведённую выше конфигурацию. Подробнее о настройке источников и приёмников см. в [документации](https://vector.dev/docs/) Vector.

    3. Убедитесь, что журналы доступа записываются в ClickHouse, выполнив следующий запрос. В таблице должны появиться журналы доступа:

    ```sql theme={null}
    SELECT * FROM nginxdb.access_logs
    ```

    <Image img="https://mintcdn.com/private-7c7dfe99/pIetLsS_hOGHqoPJ/images/integrations/data-ingestion/etl-tools/vector_01.webp?fit=max&auto=format&n=pIetLsS_hOGHqoPJ&q=85&s=17d0b62d95271cb39b27558a8391172a" size="lg" border alt="Просмотр журналов ClickHouse в табличном формате" width="2364" height="776" data-path="images/integrations/data-ingestion/etl-tools/vector_01.webp" />
  </Step>

  <Step title="Разбор журналов" id="4-parse-the-logs">
    Хранить журналы в ClickHouse удобно, однако запись каждого события в виде одной строки существенно ограничивает возможности анализа данных.
    Далее рассмотрим, как разобрать события журнала с помощью [materialized view](/docs/ru/concepts/features/materialized-views/incremental-materialized-view).

    **Materialized view** работает аналогично триггеру вставки в SQL. Когда строки данных вставляются в исходную таблицу, materialized view выполняет некоторое преобразование этих строк и вставляет результаты в целевую таблицу.
    Materialized view можно настроить для получения разобранного представления событий журнала в **access\_logs**.
    Пример одного такого события журнала приведён ниже:

    ```bash theme={null}
    192.168.208.1 - - [12/Oct/2021:15:32:43 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"
    ```

    В ClickHouse есть различные функции для разбора приведённой выше строки. Функция [`splitByWhitespace`](/docs/ru/reference/functions/regular-functions/splitting-merging-functions#splitByWhitespace) разбивает строку по пробельным символам и возвращает каждый токен в виде массива.
    Для демонстрации выполните следующую команду:

    ```sql title="Query" theme={null}
    SELECT splitByWhitespace('192.168.208.1 - - [12/Oct/2021:15:32:43 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"')
    ```

    ```text title="Response" theme={null}
    ["192.168.208.1","-","-","[12/Oct/2021:15:32:43","+0000]","\"GET","/","HTTP/1.1\"","304","0","\"-\"","\"Mozilla/5.0","(Macintosh;","Intel","Mac","OS","X","10_15_7)","AppleWebKit/537.36","(KHTML,","like","Gecko)","Chrome/93.0.4577.63","Safari/537.36\""]
    ```

    В некоторых строках есть лишние символы, а user agent (сведения о браузере) не нуждался в разборе, однако
    результирующий массив близок к тому, что требуется.

    Аналогично `splitByWhitespace`, функция [`splitByRegexp`](/docs/ru/reference/functions/regular-functions/splitting-merging-functions#splitByRegexp) разбивает строку на массив по регулярному выражению.
    Выполните следующую команду, которая возвращает две строки.

    ```sql theme={null}
    SELECT splitByRegexp('\S \d+ "([^"]*)"', '192.168.208.1 - - [12/Oct/2021:15:32:43 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"')
    ```

    Обратите внимание, что вторая возвращённая строка — это user agent, успешно извлечённый из лога:

    ```text theme={null}
    ["192.168.208.1 - - [12/Oct/2021:15:32:43 +0000] \"GET / HTTP/1.1\" 30"," \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36\""]
    ```

    Прежде чем рассмотреть финальную команду `CREATE MATERIALIZED VIEW`, ознакомимся ещё с несколькими функциями для очистки данных.
    Например, значение `RequestMethod` равно `"GET` и содержит лишнюю двойную кавычку.
    Для её удаления можно воспользоваться функцией [`trimBoth` (alias `trim`)](/docs/ru/reference/functions/regular-functions/string-functions#trimBoth):

    ```sql theme={null}
    SELECT trim(LEADING '"' FROM '"GET')
    ```

    Строка времени начинается с символа `[` и не имеет формата, который ClickHouse может разобрать как дату.
    Однако если изменить разделитель с двоеточия (**:**) на запятую (**,**), разбор выполняется корректно:

    ```sql theme={null}
    SELECT parseDateTimeBestEffort(replaceOne(trim(LEADING '[' FROM '[12/Oct/2021:15:32:43'), ':', ' '))
    ```

    Теперь можно определить materialized view.
    Приведённое ниже определение включает `POPULATE`, что означает: существующие строки в **access\_logs** будут обработаны и вставлены немедленно.
    Выполните следующий SQL-оператор:

    ```sql theme={null}
    CREATE MATERIALIZED VIEW nginxdb.access_logs_view
    (
      RemoteAddr String,
      Client String,
      RemoteUser String,
      TimeLocal DateTime,
      RequestMethod String,
      Request String,
      HttpVersion String,
      Status Int32,
      BytesSent Int64,
      UserAgent String
    )
    ENGINE = MergeTree()
    ORDER BY RemoteAddr
    POPULATE AS
    WITH
      splitByWhitespace(message) as split,
      splitByRegexp('\S \d+ "([^"]*)"', message) as referer
    SELECT
      split[1] AS RemoteAddr,
      split[2] AS Client,
      split[3] AS RemoteUser,
      parseDateTimeBestEffort(replaceOne(trim(LEADING '[' FROM split[4]), ':', ' ')) AS TimeLocal,
      trim(LEADING '"' FROM split[6]) AS RequestMethod,
      split[7] AS Request,
      trim(TRAILING '"' FROM split[8]) AS HttpVersion,
      split[9] AS Status,
      split[10] AS BytesSent,
      trim(BOTH '"' from referer[2]) AS UserAgent
    FROM
      (SELECT message FROM nginxdb.access_logs)
    ```

    Теперь убедитесь, что всё работает.
    Журналы доступа должны быть аккуратно разобраны по столбцам:

    ```sql theme={null}
    SELECT * FROM nginxdb.access_logs_view
    ```

    <Image img="https://mintcdn.com/private-7c7dfe99/pIetLsS_hOGHqoPJ/images/integrations/data-ingestion/etl-tools/vector_02.webp?fit=max&auto=format&n=pIetLsS_hOGHqoPJ&q=85&s=975c0592f270bafe012c4fdbae48fb49" size="lg" border alt="Просмотр разобранных журналов ClickHouse в табличном формате" width="2824" height="1180" data-path="images/integrations/data-ingestion/etl-tools/vector_02.webp" />

    <Note>
      В уроке выше данные сохранялись в двух таблицах, но вы можете изменить исходную таблицу `nginxdb.access_logs`, чтобы использовать для неё движок таблицы [`Null`](/docs/ru/reference/engines/table-engines/special/null).
      Разобранные данные по-прежнему будут попадать в таблицу `nginxdb.access_logs_view`, но сырые данные не будут храниться в таблице.
    </Note>
  </Step>
</Steps>

> С помощью Vector, для которого нужны лишь простая установка и быстрая настройка, вы можете отправлять журналы с сервера Nginx в таблицу ClickHouse. С помощью materialized view можно разобрать эти журналы на столбцы для более удобной аналитики.
