阅读视图

使用Filebeat采集Nginx日志到ES

filebeat是传统elk组件中logstach的升级替代,能够高性能的采集一些中间件的日志到es中,供检索分析。

1.安装filebeat

首先要安装filebeat到nginx所在服务器,因为我的服务器是rocky linux属于redhat系,故这里通过yum安装,先设置安装源

导入GPG-KEY

rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch

新建一个elastic.repo文件在/etc/yum.repos.d下,并粘贴安装源地址

vim /etc/yum.repos.d/elastic.repo

[elastic-9.x]name=Elastic repository for 9.x packagesbaseurl=https://artifacts.elastic.co/packages/9.x/yumgpgcheck=1gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearchenabled=1autorefresh=1type=rpm-md

接下来执行安装,直到安装完成

yum install filebeat -y

2.设置nginx和filebeat

首先确认nginx的日志路径和日志格式,一般日志路径默认就是:

  • /var/log/nginx/access.log 常规访问日志
  • /var/log/nginx/error.log 错误日志

在nginx.conf配置文件中,默认的日志格式是:

log_format  main  ' $remote_addr - $remote_user [$time_local] "$request" '                  '$status $body_bytes_sent "$http_referer" '                  '"$http_user_agent" "$http_x_forwarded_for"';

为了区分各个主机的访问记录,我选择增加一个主机的字段:$host

log_format  main  '$host $remote_addr - $remote_user [$time_local] "$request" '                  '$status $body_bytes_sent "$http_referer" '                  '"$http_user_agent" "$http_x_forwarded_for"';

亲测filebeat可以识别上述的日志格式,自动提取有效信息

然后设置filebeat,通过yum安装的filebeat,默认全局配置文件位于/etc/filebeat/filebeat.yml,有这样几项需要修改

output.elasticsearch:  # 改成自己es地址和端口  hosts: ["localhost:9016"]  # 改成自己的索引格式  index: "nginx-logs-%{+yyyy.MM.dd}"  # 通信协议按需要修改  protocol: "http"  # es用户名密码,必须设置  username: "elastic"  password: "***************"# 需要新增这两项,索引数据格式模板名称setup.template.name: "nginx-logs"setup.template.pattern: "nginx-logs-*"

然后对nginx的采集功能进行设置,filebeat支持很多中间件的日志采集,通过yum安装的filebeat,默认的各中间件的采集配置文件位于:/etc/filebeat/modules.d/

首先要将默认的nginx配置文件nginx.yml.disabled复制出一份nginx.yml,因为最后filebeat只会自动导入读取.yml结尾的文件

cp /etc/filebeat/modules.d/nginx.yml.disabled /etc/filebeat/modules.d/nginx.yml

vim编辑/etc/filebeat/modules.d/nginx.yml配置文件,针对nginx的采集进行配置

- module: nginx  # 打开常规访问日志采集,指定日志路径  access:    enabled: true    var.paths: ["/var/log/nginx/access.log"]    var.timezone: "Asia/Shanghai"  # 打开错误日志采集,指定日志路径  error:    enabled: true    var.paths: ["/var/log/nginx/error.log"]    var.timezone: "Asia/Shanghai"

都修改完成后,通过filebeat test config命令,验证配置文件是否有语法错误

[root@VM-0-3-rockylinux ~]# filebeat test configConfig OK

然后启动filebeat,并且能看到进程,启动成功

[root@VM-0-3-rockylinux ~]# systemctl start filebeat[root@VM-0-3-rockylinux ~]# ps -ef | grep filebeatroot      279214       1  0 Apr17 ?        00:00:09 /usr/share/filebeat/bin/filebeat --environment systemd -c /etc/filebeat/filebeat.yml --path.home /usr/share/filebeat --path.config /etc/filebeat --path.data /var/lib/filebeat --path.logs /var/log/filebeatroot      484905  454652  0 14:33 pts/2    00:00:00 grep --color=auto filebeat

3.查看索引

登录kibana,打开开发工具,就能看到filebeat建的索引和采集到的日志了,还可以根据业务需要制作图表等

还可以通过检索,通过链接和访问次数进行聚合,查出一些攻击和刺探的恶意请求,例如:

GET /nginx-logs-2026.04.18/_search{  "size": 0,   "aggs": {    "domain_counts": {      "terms": {        "field": "url.domain",       "size": 20000      },      "aggs": {        "domains_per_ip": {          "terms": {            "field": "source.ip",            "size": 20000                      },          "aggs": {            "domains_per_path": {              "terms": {                "field": "url.path",                "size": 20000                              }            }          }        }      }    }  }}
  •  

Python的函数

本文未完待续

1.定义

1.函数代码以def关键字开头,后接函数标识符名称和圆括号()
2.任何传入的参数和自变量必须放在圆括号内,圆括号中定义参数
3.函数内容以冒号:开始,需要缩进
4.return表达式结束函数,返回一个值给调用方,不带表达式的return相当于返回None
5.若暂无函数实现,函数体内写一个pass关键字,可避免语法错误

def 函数名(参数列表):    函数体    return 返回值

例:

def greet(name, age):    print("Hello World")

2.类型注解 Type Hints

Python函数参数不需要声明类型,解释器会自动推断,但是会带来难以理解,隐藏BUG,开发效率低下等问题,因此类型注解通过引入可选的类型信息解决这些问题,明确指出函数和返回值的类型,让代码更加健壮可维护。

在实现复杂逻辑和对外提供公共接口时,都应当使用类型注解

例:
func.py

def greet(name: str, age: int) -> str:    return f"Hello  World {name}, age {age}"

但是,调用时仍然可以不遵守

func.py

def greet(name: str, age: int) -> str:    return f"Hello  World {name}, age {age}"if __name__ == '__main__':    print( greet('lzj', 12.6) )

此时可以通过mypy工具包,对我们的代码进行检测,会检测出问题

先安装mypy工具 pip install mypy,再进行检测

PS D:\PycharmProjects\python-lang-test> mypy .\func\func.pyfunc\func.py:5: error: Argument 2 to "greet" has incompatible type "float"; expected "int"  [arg-type]Found 1 error in 1 file (checked 1 source file)

如果要彻底避免,需要进行严格类型检查

def add(a: int, b: int) -> int:    if not isinstance(a, int):        raise TypeError(f"参数a期望类型int,实际传入{type(a).__name__}")    if not isinstance(b, int):        raise TypeError(f"参数b期望类型int,实际传入{type(b).__name__}")    return a + bresult2 = add('5', 3)print(result2)
Traceback (most recent call last):  File "D:\PycharmProjects\python-lang-test\func\func.py", line 9, in <module>    result2 = add('5', 3)  File "D:\PycharmProjects\python-lang-test\func\func.py", line 4, in add    raise TypeError(f"参数a期望类型int,实际传入{type(a).__name__}")TypeError: 参数a期望类型int,实际传入str

在函数返回值是某种特定类型或None时使用Optional,等价于Union[具体类型, None]

例:dic.get(name)可能返回字典的值,也可能在没有值时返回None

from typing import Optionaldef age(name: str) -> Optional[int]:    dic = {'a':16, 'b':17, 'c':18}    return dic.get(name)if __name__ == '__main__':    print(age('dd'))    print(age('a'))
None16

3.参数

3.1 位置参数

以正确的形式传入函数,数量要和声明时保持一致

from typing import Optionaldef fun(age:int) -> Optional[int]:    print(age)    return agefun(10)

3.2 关键字参数

函数调用使用关键字参数来确定传入的参数值

def fun(age:int, name:str) :    print(age)    print(name)fun(name='lzj', age=10)
10lzj

3.3 默认参数

调用函数时,如果没有传递参数,则会使用默认参数

def fun(name:str = "a", age:int = 25) :    print(f"fun---name:{name} age:{age}")fun()fun("zhangliang")fun("wangqiang", 28)
fun---name:a age:25fun---name:zhangliang age:25fun---name:wangqiang age:28

3.4 “*”可变位置参数

加了*的参数会以元组的形式传入,一般这种参数放在参数列表后面

格式:

def fun([普通参数], *var_args_tuple) :    函数体

例:

def print_info(num, *var_tuple):    print(num)    print(var_tuple)print_info(70,1,2,3,4,5)print_info(80)print_info(90,*(1,2,3,4,5))
70(1, 2, 3, 4, 5)80()90(1, 2, 3, 4, 5)

如果后面还有参数,必须通过关键字参数传入,如果没有给不定长的参数传参,那么得到的是空元组

def print_info1(seq, *var_tuple, age) :    print(seq)    print(var_tuple)    print(age)print_info1(1,20,30,40,50, age = 25)print_info1(70, age = 29)
1(20, 30, 40, 50)2570()29

3.5 “**”可变关键字参数

加了两个星号的参数,会以字典的形式传入,“**”可变参数的后面不能再有其他参数,因此“**”可变参数往往在“*”可变参数的后面(如果有的话),并且作为整个参数列表的最后一个参数

调用时,直接在参数列表直接传入键值对

def func(nid, **info):    print(nid)    print(info)    if __name__ == '__main__':    func(10, name='苗苗', age=22)    func(11, name='欣欣', age=27)
10{'name': '苗苗', 'age': 22}11{'name': '欣欣', 'age': 27}

如果要传字典,需要用**前缀,否则报错

if __name__ == '__main__':    dic = {'name':'苗苗', 'age': 18}    #func(10, dic) ❌    func(10, **dic) #✅    func(10, **{'name':'苗苗', 'age': 18}) #✅

3.6.参数解包/打包

上面出现的*() **{}就是一种解包/打包的写法

概念

  • 打包(Packing):将多个参数合并成一个数据结构(元组或字典)
  • 解包(Unpacking):将一个数据结构拆解成多个参数

异同:

语法含义类型使用场景
*args可变位置参数元组不确定数量的位置参数
**kwargs可变关键字参数字典不确定数量的关键字参数
*args, **kwargs组合使用-通用函数包装、装饰器、继承

总结

  • *args:接收多余的位置参数(arguments)
  • **kwargs:接收多余的关键字参数(keyword arguments)

例:

def func(a, *args, **kwargs):    print(a)    print(args)    print(kwargs)func(100,1,2,3,k1=20, k2=30)func(200,*(1,2,3),**{"k1":20, "k2":30})
100(1, 2, 3){'k1': 20, 'k2': 30}200(1, 2, 3){'k1': 20, 'k2': 30}

例:参数解包

传递参数时,可以将多个实参用*组合为元组,传入形参

def sum_num(a, b, c):    return a+b+cif __name__ == '__main__':    print( sum_num(1, 2, 3) ) #6    print( sum_num( *(1, 2, 3) ) ) #6    let_tuple = (1, 2, 3)    print( sum_num( *let_tuple ) ) #6

4.Lambda匿名函数

python使用lambda来创建匿名函数

  • lambda是一种小型的内联函数,通常只有一行代码
  • 匿名的,只能通过赋值给变量或作为参数传递给其他函数的方式来使用
  • 可以有任意数量的参数,但是只能有一个表达式
  • 不需要使用def关键字定义
  • 通常用于编写简单的单行的函数
  • 通常在函数需要作为参数传递的情况下使用

与java的lambda的区别:通常只有一行代码


组成:

lambda arguments : expression
  • lambda 是python的关键字
  • arguments 参数列表
  • expression 表达式,用于计算并返回结果

例:

某种操作函数operator,传入待操作数a, b以及操作规则func,将1和2以及一个加法计算规则add()传入,令operator函数对两个数进行加法

def add(a, b):    return a + bdef operator(a, b, func):    return func(a, b)print( operator(1, 2, add) ) # 3

add()就无需单独再定义函数,可以匿名成为一个加法计算规则的lambda:lambda x, y: x + y

print( operator(1, 2, lambda x, y: x + y ) ) #3

lambda可以赋给变量,称为函数变量,函数变量保存的是函数类型的值,对函数变量进行调用(加小括号),才能得到对应函数的返回值类型的值

例:

f = lambda: "Hello, world!"print(f) # <function <lambda> at 0x000002A2226B7C40>print(f())  # Hello, world!
x = lambda a, b, c : a + b + cprint(x(5, 6, 2)) #13

在python中,lambda通常可以和一些内置函数配合使用,实现一些效果

numbers = [1, 2, 3, 4, 5, 6, 7, 8]even_numbers = list(filter(lambda x: x % 2 == 0, numbers))print(even_numbers)  # 有三名学生的姓名和年龄,按年龄排序student_list = [{"name": "z3", "age": 36}, {"name": "li4", "age": 14}, {"name": "w5", "age": 27}]print(sorted(student_list, key=lambda x: x["age"]))# map() 的主要作用是将给定的函数批量应用到可迭代对象的每个元素上,实现数据转换map_result = map(lambda x: x * x, [0, 1, 3, 7, 9])print(list(map_result)) # filter() 的主要作用是将给定的函数**批量应用**到可迭代对象的每个元素上,实现数据过滤filter_result = filter(lambda x: x >= 0, [-0, -1, -3, 7, 9])print(list(filter_result))  
[2, 4, 6, 8][{'name': 'li4', 'age': 14}, {'name': 'w5', 'age': 27}, {'name': 'z3', 'age': 36}][0, 1, 9, 49, 81][0, 7, 9]

5.闭包 (Closure)

闭包是python实现函数式编程的重要基础

定义:内部函数引用了嵌套它的外部函数的变量或参数,且内部函数被返回或暴露出去,从而保留了外部函数作用域(即便已经调用完成)的函数对象。

例:定义一个函数lazy_sum(),嵌套一个内部函数inner(),并将内部函数inner()作为返回值返回,并赋值给了num,但是是个函数对象,只有执行num(),内部函数inner()才会执行,将外部函数的参数*args进行累加。在这个过程中lazy_sum()的执行完成后,并没有被释放,参数*args也被保留下来,什么时候内部函数执行了,什么时候真正完成调用。

def lazy_sum(*args):    def inner():        result = 0        for n in args:            result = result + n        return result    #返回函数    return innernum = lazy_sum(11, 12)print(num) # <function lazy_sum.<locals>.inner at 0x0000023A15FD62A0>print(num()) # 23print(num.__closure__) #闭包保存的变量元组 (<cell at 0x00000241568DB5E0: tuple object at 0x00000241568E8940>,)print(num.__closure__[0].cell_contents) # 闭包保存的变量 (11, 12)

例:带参数

def hello(s1):    def world(s2):        return s1+' '+s2    #返回函数    return worldfun = hello('hello')print(fun('world')) #hello worldprint( hello('hello')('world') ) #hello world

总结:

  • 函数要嵌套,支持多层
  • 内部函数要引用外部函数的局部变量
  • 外部函数将内部函数返回,或作为参数传递

利弊:

  • 优点: 封装隐藏数据和持久化状态,简化参数传递,适合实现轻量级状态保持场景
  • 缺点: 内存占用过高,可读性和可调试性差,存在循环变量捕获陷阱,使用时需要避免持有占用大空间的数据

6.装饰器(Decorators)

装饰器类似Spring AOP,前置/后置通知

装饰器(decorators)是Python中的一种高级功能,它允许你动态地修改函数或类的行为。

装饰器本质上是一种函数,它接受一个函数作为参数并返回一个新的函数或修改原来的函数,可以实现不修改被修饰对象的源码和调用方式的前提下,为其额外添加功能(日志,计时,权限校验等)。

执行原理:

  1. 绑定原函数
  2. 定义额外功能
  3. 替换,返回包装函数
  4. 调用,执行原逻辑+增强逻辑

例:装饰器的实现,使用aop()函数增强hello()函数,aop()函数替换掉原hello()函数,直接调用aop()函数

def aop(func):    def wrapper():        print("before")        func()        print("after")    return wrapperdef hello() :    print('hello world')if __name__ == '__main__':    aop(hello)()
beforehello worldafter

例:简化写法,@加上一个闭包函数,就能构成一个装饰器函数,语法类似Java中的注解

def aop(func):    def wrapper():        print("before")        func()        print("after")    return wrapper@aopdef hello() :    print('hello world')if __name__ == '__main__':    hello()
beforehello worldafter

例:带参数的装饰器

def aop(func):    def wrapper(*args, **kwargs):        print("before")        func(*args, **kwargs)        print("after")    return wrapper@aopdef hello(name) :    print(f'hello,{name}')if __name__ == '__main__':    hello('sxh')
  •