收集转发日志组件 fluentd 语法简介


请添加图片描述 请添加图片描述 请添加图片描述

Fluentd事件

  1. 每个输入的事件会带有一个tag
  2. Fluentd通过tag匹配output
  3. Fluentd发送事件到匹配的output
  4. Fluentd支持多个数据源和数据输出
  5. 通过过滤器,事件可以被重新触发

🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻

Plugins:

插件介绍 :

Fluentd 有一个非常活跃社区,提供了大量的插件,你可以在这里看到大多数常见插件的列表:List of All Plugins。

Fluentd 支持 7 种类型的插件:

  • Input:事件流入口
  • Parser:修改 Input 插件中事件格式,用于 Source
  • Filter: 修改事件流,用于 Filter
  • Output:输出插件,用于 Match
  • Formatter:修改 Output 插件中事件流的格式,用于 Match
  • Buffer:在 Output 插件中指定 buffer,用于 Match
  • Storage:将插件状态存入内存或数据库,可用于 Source、Filter 和 Match,需要插件支持 storage 命令

插件参数 Parameters

不同的插件都可以设定不同的参数,拿最简单的 forward 举个例子:

<source>
  @type http
  port 9880
</source>

其中 @typeport 都是参数,一个指明了插件的名字,另一个指明了监听的端口。
fluentd 里有两种类型的参数:

  • 默认参数:以 @ 开头的都是默认参数;
  • 插件参数:其余的参数都是插件参数,为插件做配置,可以在插件文档里查阅。

默认参数:
这些参数是系统保留的并且带有@前缀。

  • @type: 指定插件的类型。
  • @id: 指定插件的id。
  • @label:用来指定标签。
  • @log_level:用来指定每个插件的log级别。

其他插件参数:
拿 tail 举个例子,我们可以查阅 文档, 可以看到它有 tag, path, exclude_path, … 等一系列的参数,比如其中 tag 就可以为日志流打上供 match 使用的 tag。

语法介绍:

配置文件语法 😒

在这里插入图片描述
官方文档: https://docs.fluentd.org/input

source 😎

“source”: where all the data come from //定义数据源(input)

source:就是输入源(input),比较常用的有两个插件一个是http,一个是forward(tcp) 模式,比如我们可以定义 http 和 forward 的数据源。http数据源可以通过http协议来接收数据,forward可以通过tcp协议来接收数据,除了这两个外,fluentd 还有大量的支持各种协议或方式的 source 插件。

# Receive events from 24224/tcp 
// 从24224/tcp 中接收事件,tcp模式
# This is used by log forwarding and the fluent-cat command  
// 使用日志转发和fluent-cat 命令
<source>
  @type forward  # 这个就是表示插件
  port 24224
</source>

# http://this.host:9880/myapp.access?json={"event":"data"}
<source>
  @type http
  port 9880
</source>
// 每一个具体的插件都包含其特有的参数,比如上例中 port 就是一个参数,当你要使用一个 source 插件的时
// 候,注意看看有哪些参数是需要配置的,然后将其写到 source directive 内。

每个source指令必须包含一个 type(类型)参数。 该参数用来指定使用哪个输入插件,比如我们还可以用tail插件来读取文件的内容

match 😊

“match”: Tell fluentd what to do //定义数据的输出目标(output)

match指令通过匹配tag字段来将事件输出到其他的系统。同样match指令也必须指定@type参数,该参数用来指定使用哪个输出插件。在下面的例子中,只有myapp.access的tag能够匹配到该输出插件。

# Receive events from 24224/tcp 
// 从24224/tcp接收
# This is used by log forwarding and the fluent-cat command 
<source>
  @type forward
  port 24224
</source>

# http://this.host:9880/myapp.access?json={"event":"data"}
<source>
  @type http
  port 9880
</source>

# Match events tagged with "myapp.access" and store them to /var/log/fluent/access.%Y-%m-%d
# Of course, you can control how you partition your datawith the time_slice_format option.
// 将标记为 myapp.access 的日志转存到文件
<match myapp.access> 
  @type file  
  path /var/log/fluent/access
</match>

match 不仅仅用来处理输出,还可以对日志事件进行一些处理后重新抛出,当成一个新的事件从新走一遍流程,比如可以用 rewrite_tag_filter 插件为日志流重新打上 tag,实现通过正则来对日志进行分流的需求:

<match app>
  // 捕获被打上了 app tag 的日志
  ...
</match>
 
<match cp>
  // 捕获被打上了 cp tag 的日志
  ...
</match>
 
<match **>
  // https://docs.fluentd.org/v0.12/articles/out_rewrite_tag_filter
  // 被打上 tag 的日志会被从头处理,从而被上面的 match 捕获,实现了日志的分流
  @type rewrite_tag_filter
  <rule>
    key log // 指定要处理的 field
    pattern ^.*\ c\.p\.\ .*  // 匹配条件 
    tag cp  // 打上 tag `cp`
  </rule>
  <rule>
    key log
    pattern ^.*
    tag app  // 其余日志打上 tag `app`
  </rule>
</match>

filter 😍

“filter”: Event processing pipeline //事件处理管道

filter:可以理解为过滤器,“filter”指令的语法和”match”指令的语法相同,但是”filter”能够在管道中被连起来处理,filter 和 match 的语法几乎完全一样,但是 filter 可以串联成 pipeline,对数据进行串行处理,最终再交给 match 输出。

流程如:Input -> filter 1 -> … -> filter N -> Output

如下为 record_transformerfilter 插件实例:

source 首先会接收到一个 {“event”:”data”} 的事件,然后该事件会首先被路由到 filter,filter 会增加一个host_param 的字段到 record 中,然后再把该事件发送到 match 中。

# http://this.host:9880/myapp.access?json={"event":"data"}
<source>
  @type http
  port 9880
</source>

<filter myapp.access>
  @type record_transformer
  <record>
    host_param "#{Socket.gethostname}"
  </record>
</filter>

<match myapp.access>
  @type file
  path /var/log/fluent/access
</match>

label 😘

用来组织filter和match,就是为了降低tag过滤的复杂性

通过”label”指令可以用来组织filter和match的内部路由。下面是一个配置的例子,由于”label”是内建的插件,所以他的参数需要以@开头。
你可以在 source 里指定 @label @<LABEL_NAME>, 这个 source 所触发的事件就会被发送给指定的 label 所包含的任务, 而不会被后续的其他任务获取到。
需要注意的是,label 一旦被声明了,就必须在后面被用到,否则会报错。

<source>
  @type forward
</source>

<source>
  // 这个任务指定了 label 为 @SYSTEM
  // 会被发送给 <label @SYSTEM>
  // 而不会被发送给下面紧跟的 filter 和 match
  @type tail
  @label @SYSTEM   // 这个input 直接进到label 哪里进行处理
</source>

<filter access.**>
  @type record_transformer
  <record>
    # ...
  </record>
</filter>
<match **>
  @type elasticsearch
  # ...
</match>

<label @SYSTEM>
// 将会接收到上面 @type tail 的 source event
  <filter var.log.middleware.**>
    @type grep
    # ...
  </filter>
  <match **>
    @type s3
    # ...
  </match>
</label>

在上面的例子中,forward的数据源的事件被路由到record_transformer filter和elasticsearch output中。tail数据源被路由到@system里面的grep filter和s3 output中。

error label 😂

@ERROR label是内建的label,用来记录emit_error_event API错误事件的。如果在配置文件里面设置了,当有相关的错误发生(比如:缓冲区已满或无效记录)的话,该错误事件就会被发送到< label @ERROR >。
使用方法和 label 一样,通过设定 <label @ERROR> 就可以接收到相关的异常

log level 😁

目前支持的日志级别参数值有:

  • fatal
  • error
  • warn
  • info
  • debug
  • trace

从上往下依次递减,当你指定了一个级别后,会捕获大于等于该级别的所有日志。
比如如果你指定 @log_level info,就会获取到 info, warn, error, fatal 级别的日志。

system 🤷‍♀️

主要设置一些系统范围配置的

  • log_level
  • suppress_repeated_stacktrace
  • emit_error_log_interval
  • suppress_config_dump
  • without_source
  • process_name (only available in system directive. No fluentdoption//只能用”system”指令指定)

在td-agent.conf文件中添加如下:

<system>
  # 等价于-qq选项
  log_level error
  # 等价于--without-source选项
  without_source
</system>
<system>
  process_name fluentd1
  # process_name用来指定Fluentd监控进程和工作进程的名字,通过ps可以看到
</system>


# ps aux | grep fluentd1
foo      45673   0.4  0.2  2523252  38620 s001  S+    7:04AM   0:00.44 worker:fluentd1
foo      45647   0.0  0.1  2481260  23700 s001  S+    7:04AM   0:00.40 supervisor:fluentd1

include 😢

就是导入(重用配置)

可以通过”@include”来导入其他的配置文件
第一步:先编写 source.conf

<source>
  @type http
  port 8887
  bind 0.0.0.0
</source>

第二步:编写td-agent.conf文件

@include ./source.conf  // 这一步:就会将上面的内容导入到这里
<filter test.cycle>
  @type grep
  <exclude>
    key action
    pattern ^login$
  </exclude>
</filter>

<label @STAGING>
  <filter test.cycle>
    @type grep
    <exclude>
      key action
      pattern ^logout$
    </exclude>
  </filter>

  <match test.cycle>
    @type stdout
  </match>
</label>

<match test.cycle>
  @type stdout
</match>

配置文件是按顺序导入的。如果使用模式匹配的话,文件是按字母顺序导入的

# If you have a.conf,b.conf,...,z.conf and a.conf / z.conf are important...
# This is bad
@include *.conf

# This is good
@include a.conf
@include config.d/*.conf
@include z.conf

如果导入的文件有顺序的要求的话,最好自己主动写导入的语句,模式匹配导入容易出错。

Routing 🤦‍♂️

source dirctive 在获取到输入后,会向 fluent 的路由抛出一个事件,这个事件包含三个要素:

  • tag
  • time
  • record

source指令把事件提交到Fluentd的路由引擎。tag是由 . 分割的字符串组成,被内部路由引擎使用。time由input插件指定,必须是Unix时间戳格式。record是一个Json对象。(强烈推荐使用小写字母、数字和下划线来命名tag,虽然其他的字符也是合法的。)

主要的是tag,用来指导方向,所要匹配的地方
例如:当我们发起一个 http://this.host:9880/myapp.access?json={“event”:“data”} 的请求时,这个 source 会抛出:

// generated by http://this.host:9880/myapp.access?json={"event":"data"}
tag: myapp.access
time: (current time)
record: {"event":"data"}

通配符

filter 和 match 标签中的tag 通配符号
*匹配满足一个tag部分的事件, 比如: a. *, 它将匹配a.b这样的tag, 但是不会处理a或者a.b.c这类tag

** 匹配满足0个或多个tag部分,比如: a. **, 它将匹配a, a.b, a.b.c这三种tag

{X,Y,Z}匹配满足X,Y或者Z的tag, 比如: {a, b} 将匹配a或者b,但是不会匹配c。这种格式也可以和通配符组合使用,比如a.{b.c}.或a.{b.c}.

#{…}会将里面的内容当作ruby表达式处理:比如

<match "app.#{ENV['FLUENTD_TAG']}">
  @type stdout
</match>
//如果设置了环境变量 FLUENTD_TAG 为 dev,那上面等价于 app.dev

当指定了多个模式时(使用一个或多个空格分开),只要满足其中任意一个就行。比如:

<match a b> // 匹配 a 和 b
<match a.** b.*>  // 匹配 a, a.b, a.b.c, b.d等

<match a.*>
<match **>
<match a.{b,c}>
<match a.* b.*>

配置文件中的参数类型

每个Fluentd插件都有一组参数。例如,in_tail具有rotate_wait和pos_file等参数。每个参数都有一个与之关联的特定类型。它们的定义如下:

  • string:字符串,最常见的格式,string类型可以有三种形式:不带引号的字符串、带单引号的字符串和带双引号的字符串。
  • integer:整数
  • float:浮点数
  • size:大小,仅支持整数,该类型用来解析成有多少个字节。可以在整数后面加上k/K、m/M、g/G、t/T,对应的是计算机学科的度量单位。比如:12k表示为12*1024后的数值。
    k 或 K
    m 或 M
    g 或 G
    t 或 T
  • time:时间,也只支持整数,可以在浮点数后面加上s、m、h和d分别表示为秒、分、小时、天。可以用0.1表示100ms。
    s 或 S
    m 或 M
    h 或 H
    d 或 D
  • array:按照 JSON array 解析
    完整格式的写法: [“key1”, “key2”]
    简写: key1,key2
  • hash:按照 JSON object 解析
    完整格式的写法:{“key1”:“value1”, “key2”:“value2”}
    简写:key1:value1,key2:value2

多个match之间的顺序

当有多个match, 需要注意一下它们的顺序, 如下面的例子,第二个match永远也不会生效

// ** matches all tags. Bad :(
<match **>
  @type blackhole_plugin
</match>

<match myapp.access>
  @type file
  path /var/log/fluent/access
</match>

如果将filter放在match之后,那么它也永远不会生效,正确的用法如下:

# You should NOT put this <filter> block after the <match> block below.
# If you do, Fluentd will just emit events without applying the filter.
<filter myapp.access>
  @type record_transformer
  ...
</filter>

<match myapp.access>
  @type file
  path /var/log/fluent/access
</match>

检查配置文件是否可用

通过–dry-run选项,可以在不启动插件的情况下检查配置文件。

fluentd --dry-run -c fluent.conf

格式

双引号包起来的字符串、数组和哈希类型支持多行

str_param "foo  # This line is converted to "foo\nbar". NL is kept in the parameter
 bar"
array_param [
  "a", "b"
]
hash_param {
  "k":"v",
  "k1":10
}

如果想让[或者{开头的字符串不被解析成数组或者对象,则需要用’或者“把该字符串包起来。

<match **>
  @type mail
  subject "[CRITICAL] foo's alert system"
</match>
<match tag>
  @type map
  map '[["code." + tag, time, { "code" => record["code"].to_i}], ["time." + tag, time, { "time" => record["time"].to_i}]]'
  multi true
</match>

嵌入Ruby代码

可以在”包住的#{}里面执行Ruby代码,这可以用来获取一些机器的信息,比如hostname。

host_param "#{hostname}"  # This is same with Socket.gethostname
@id "out_foo#{worker_id}" # This is same with ENV["SERVERENGINE_WORKER_ID"]

在双引号字符串中,\是转义字符

\被解释为转义字符。你需要用\来设置”,\r,\n,\t,\或双引号字符串中的多个字符。

str_param "foo\nbar" # \n is interpreted as actual LF character

🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值