> ## 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.

> 如何使用 Vector 持续读取日志文件并将其导入 ClickHouse

# 集成 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 日志文件进行 tail 并将其发送到 ClickHouse。
下面的步骤同样适用于 tail 任何类型的日志文件。

**前置条件：**

* 你已成功运行 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()** (空 Tuple) 。
    </Note>
  </Step>

  <Step title="配置 Nginx" id="2--configure-nginx">
    在这一步中，您将了解如何配置 Nginx 日志。

    1. 以下 `access_log` 属性会以 **combined** 格式将日志写入 `/var/log/nginx/my_access.log`。
       该值应放在 `nginx.conf` 文件的 `http` 部分中：

    ```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.conf`，请务必重启 Nginx。

    3. 访问 Web 服务器上的页面，以在访问日志中生成一些日志事件。
       **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 用于收集、转换并将日志、指标和链路追踪 (统称为 **sources**) 路由到许多不同的厂商平台 (称为 **sinks**) ，并且开箱即用地支持 ClickHouse。
    sources 和 sinks 在名为 **vector.toml** 的配置文件中定义。

    1. 以下 **vector.toml** 文件定义了一个类型为 **file** 的 **source**，用于持续跟踪 **my\_access.log** 文件末尾追加的内容；它还将上面定义的 **access\_logs** 表定义为一个 **sink**：

    ```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。有关如何定义 sources 和 sinks 的更多信息，请参阅 Vector [文档](https://vector.dev/docs/)。

    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/zh/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/zh/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/zh/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` (别名 `trim`)](/docs/zh/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/zh/reference/engines/table-engines/special/null) 表引擎。
      解析后的数据仍会进入 `nginxdb.access_logs_view` 表，但原始数据不会存储到任何表中。
    </Note>
  </Step>
</Steps>

> 使用 Vector 时，只需简单安装并快速配置，即可将 Nginx 服务器的日志发送到 ClickHouse 中的表。通过使用 materialized view，您可以将这些日志解析到多个列中，以便更轻松地进行分析。
