目录
Fluentd事件
- 每个输入的事件会带有一个tag
- Fluentd通过tag匹配output
- Fluentd发送事件到匹配的output
- Fluentd支持多个数据源和数据输出
- 通过过滤器,事件可以被重新触发
🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻
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>
其中 @type、port 都是参数,一个指明了插件的名字,另一个指明了监听的端口。
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
🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼🥛🧃☕🫖🍵🧉🍶🍾🍷🍸🍹🍺🍻🍪🎂🍰🍩🍝🥣🥧🍦🍧🍲🥘🧆🍢🥮🍭🍡🍮🍯🍼