24 KiB
title, date, isGreat
| title | date | isGreat |
|---|---|---|
| OpenObserve部署和使用 | 2025-11-26 | true |
一、基础概念
1.1 Fluent Bit
Fluent Bit 是一个开源的、轻量级、高性能的日志处理器和转发器。Fluent Bit 的核心任务是:从各种来源收集日志、指标和追踪数据,进行处理和过滤,然后将其发送到一个或多个目的地。
1.1.1 特点
- 极致的轻量级
Fluent Bit 是用 C 语言编写的,运行时内存占用极低(通常只有几百 KB),CPU 使用率也非常低。这使得它成为在资源受限环境(如边缘计算、IoT 设备、容器)中运行的理想选择。 - 高性能
其核心架构经过高度优化,处理数据的速度极快,能够轻松应对高吞吐量的日志流。 - 可插拔架构
Fluent Bit 的功能通过“插件”来扩展。
1.1.2 插件类型
- Input(输入)
定义数据来源,例如从文件、标准输入、TCP/UDP、系统指标(CPU/内存)、Docker 容器、Windows 事件日志等收集数据。 - Filter(过滤器)
在数据传递过程中进行处理,例如解析(如将非结构化文本解析为 JSON)、丰富(如添加 Kubernetes 元数据)、修改(如重命名字段)、采样等。 - Output(输出)
定义数据目的地,例如 Elasticsearch、Splunk、Kafka、Datadog、Loki、S3、另一个 Fluentd/Fluent Bit 实例等。
::: tip 常用Input插件:
- Forward
适用于Fluent Bit / Fluentd 节点,传输协议为私有二进制协议。支持压缩和重连,传输效率极高,但客户端必须使用特定的库。 - TCP
适用于能建立 Socket 连接的程序,传输协议为TCP协议,只负责接收字节流,通常需要配合解析器(Parser)来处理格式。 - HTTP
通过标准的 HTTP POST 请求接收 JSON 数据,传输协议为HTTP协议,兼容性最好,任何编程语言都能轻松发送 HTTP 请求。 :::
1.1.3 核心架构
- Input(输入)
一个或多个输入插件持续地从数据源收集数据。每条数据被读入后,会带上时间戳和标签等信息,被包装成一个内部的事件记录。 - Parser(解析器)
(可选)输入插件可以使用解析器,将非结构化的日志数据(如 Nginx 访问日志)转换为结构化的 JSON 格式。 - Filter(过滤)
(可选)数据会经过一个或多个过滤器插件。这些插件可以修改、丰富或删除记录。过滤器是按顺序执行的。 - Router(路由)
根据每条记录的标签(Tag),路由器将其匹配到相应的输出插件。一个记录可以被路由到多个输出(多路复用)。 - Buffer(缓冲)
在发送到输出之前,数据可能会在内存或文件系统中进行缓冲。 - Output(输出)
最终,输出插件将处理好的数据发送到配置的目的地。
1.1.4 安装
sudo sh -c 'curl https://packages.fluentbit.io/fluentbit.key | gpg --dearmor > /usr/share/keyrings/fluentbit-keyring.gpg'
CODENAME=$(lsb_release -cs) && echo "deb [signed-by=/usr/share/keyrings/fluentbit-keyring.gpg] https://packages.fluentbit.io/ubuntu/$CODENAME $CODENAME main" | sudo tee /etc/apt/sources.list.d/fluent-bit.list
apt-get update
apt-get install fluent-bit
systemctl start fluent-bit.service
systemctl enable fluent-bit.service
::: tip
默认安装位置为:/opt/fluent-bit/bin/fluent-bit,默认配置文件位置为:/etc/fluent-bit。
:::
1.2 OpenObserve
OpenObserve 是一个开源的、云原生的可观测性数据平台,旨在处理日志、指标、追踪和性能数据。它的目标是成为 Elasticsearch/Logstash/Kibana (ELK)、Grafana Loki 和 Datadog 等流行解决方案的更简单、更经济高效、更高性能的替代品。
OpenObserve 是一个“一体化”的平台,你可以将所有的可观测性数据(日志、指标、追踪)都发送到它这里,然后通过统一的界面进行搜索、分析和可视化。
二、Docker部署
services:
fluent-bit:
image: fluent/fluent-bit:4.0.7
container_name: fluent-bit
restart: unless-stopped
volumes:
- ./fluent-bit/config:/fluent-bit/etc
ports:
- "24422:24422"
environment:
- TZ=Asia/Shanghai
networks:
- devops
openobserve:
image: public.ecr.aws/zinclabs/openobserve:v0.14.0
container_name: openobserve
restart: unless-stopped
volumes:
- ./openobserve-data:/data
environment:
- ZO_ROOT_USER_EMAIL=1556464090@qq.com
- ZO_ROOT_USER_PASSWORD=yourpassword
- TZ=Asia/Shanghai
ports:
- "5080:5080"
networks:
- devops
networks:
devops:
driver: bridge
volumes:
openobserve-data:
driver: local
fluent bit配置文件fluent-bit.conf:
[SERVICE]
Flush 5
Log_Level info
Daemon off
Parsers_File parsers.conf
[INPUT]
Name forward
Listen 0.0.0.0
Port 24422
Tag external
[OUTPUT]
Name http
Match *
Host openobserve
Port 5080
URI /api/default/default/_json
http_user 1556464090@qq.com
http_passwd yourpassword
Format json
json_date_key timestamp
json_date_format iso8601
INPUT为日志输入源,使用Forward插件,监听24422端口。
OUTPUT为日志输出源,将日志发送到openobserve中(这里使用的是容器名称),URI表示openobserve接收数据的api路径,default/default表示组织名称/流名称,可以更改为其他的名称。_json表示以json的格式。
Match *表示接收所有的INPUT,也可以根据INPUT的tag来区分分发到哪个OUTPUT。
三、数据接入
3.1 SpringBoot
3.1.1 安装依赖
<properties>
<logback-more-appenders.version>1.8.8</logback-more-appenders.version>
<fluency-fluentd.version>2.7.0</fluency-fluentd.version>
</properties>
<dependencies>
<!-- SpringBoot 日志启动 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<!-- 扩展 logback appender -->
<dependency>
<groupId>com.sndyuk</groupId>
<artifactId>logback-more-appenders</artifactId>
<version>${logback-more-appenders.version}</version>
</dependency>
<!-- Fluentd 日志搜集和转发 -->
<dependency>
<groupId>org.komamitsu</groupId>
<artifactId>fluency-fluentd</artifactId>
<version>${fluency-fluentd.version}</version>
</dependency>
</dependencies>
3.1.2 日志配置
logging:
config: classpath:logback-spring.xml
fluentd:
host: host.docker.internal
port: 24224
level:
root: info
3.1.3 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds">
<!-- 自定义属性 -->
<springProperty scope="context" name="fluentdHost" source="logging.fluentd.host" defaultValue="localhost"/>
<springProperty scope="context" name="fluentdPort" source="logging.fluentd.port" defaultValue="24224"/>
<springProperty scope="context" name="topic" source="spring.application.name" defaultValue="default"/>
<!-- log路径 -->
<property name="log.path" value="logs/${topic}"/>
<!--控制台日志格式:彩色日志-->
<!-- magenta:洋红 -->
<!-- boldMagenta:粗红-->
<!-- cyan:青色 -->
<!-- white:白色 -->
<!-- magenta:洋红 -->
<property name="CONSOLE_LOG_PATTERN"
value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
<!--文件日志格式-->
<property name="FILE_LOG_PATTERN"
value="%date{yyyy-MM-dd HH:mm:ss} |%-5level |%thread |%file:%line |%logger |%msg%n"/>
<!--编码-->
<property name="ENCODING"
value="UTF-8"/>
<!--输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!--日志级别-->
<level>DEBUG</level>
</filter>
<encoder>
<!--日志格式-->
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!--日志字符集-->
<charset>${ENCODING}</charset>
</encoder>
</appender>
<!--输出到文件-->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--日志过滤器:此日志文件只记录INFO级别的-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_info.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 日志文件的最大大小 -->
<maxFileSize>100MB</maxFileSize>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志过滤器:此日志文件只记录WARN级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_warn.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 日志文件的最大大小 -->
<maxFileSize>100MB</maxFileSize>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志过滤器:此日志文件只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_error.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 日志文件的最大大小 -->
<maxFileSize>100MB</maxFileSize>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
<!--输出到FLUENCY-->
<appender name="FLUENCY" class="ch.qos.logback.more.appenders.FluencyLogbackAppender">
<remoteHost>${fluentdHost}</remoteHost>
<port>${fluentdPort}</port>
<additionalField>
<key>topic</key>
<value>${topic}</value>
</additionalField>
<ignoredField>caller</ignoredField>
<useEventTime>true</useEventTime>
</appender>
<!-- 开发环境 -->
<springProfile name="dev">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>
<!-- 生产环境 -->
<springProfile name="prod">
<root level="INFO">
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</springProfile>
<!-- 容器环境 -->
<springProfile name="docker">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FLUENCY"/>
</root>
</springProfile>
</configuration>
::: tip
这里是用docker部署的,如果是非docker部署,host为服务器ip地址。
:::
3.2 .NET Core
3.2.1 安装Nuget包
安装NLog.Extensions.Hosting和NLog.Targets.Network包。
::: tip
NLog.Targets.Fluentd这个包也可以,但是无法定制FluentBit的消息格式。
:::
3.2.2 使用NLog
var host = Host.CreateDefaultBuilder(args)
.UseNLog()
.Build();
3.2.3 配置NLog
在项目目录创建NLog.config并设置复制到输出目录为始终复制。
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true">
<extensions>
<add assembly="NLog.Targets.Network" />
</extensions>
<targets async="true">
<target name="fluentbit" xsi:type="Network"
newLine="true" lineEnding="LF"
address="tcp://ip:5170">
<layout xsi:type="JsonLayout">
<attribute name="topic" layout="message-hub"/>
<attribute name="timestamp" layout="${longdate:universalTime=true}"/>
<attribute name="level" layout="${level:uppercase=true}"/>
<attribute name="logger" layout="${logger}"/>
<attribute name="message" layout="${message}"/>
<attribute name="throwable" layout="${exception:format=ToString}"/>
</layout>
</target>
<target name="stdout"
xsi:type="Console"
layout="${longdate} ${level:uppercase=true}: ${logger} - ${message}${onexception:${newline}${exception:format=ToString}}" />
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="stdout, fluentbit"/>
</rules>
</nlog>
::: tip
这里的topic为项目名称。
:::
3.2.3 配置插件
在FluentBit中增加TCP插件:
[INPUT]
Name tcp
Listen 0.0.0.0
Port 5170
Tag tcp
::: tip
docker-compose文件中也要增加5170端口映射。
:::
3.3 Python
3.3.1 安装依赖
pip install fluent-logger
3.3.2 配置文件
FLUENT_HOST=ip
FLUENT_PORT=24224
FLUENT_TAG=test-server
3.3.3 日志配置
import logging
from datetime import datetime
from fluent import sender, event
from dotenv import load_dotenv
import os
load_dotenv()
# Fluent-bit配置
FLUENT_HOST = os.getenv("FLUENT_HOST", "localhost")
FLUENT_PORT = int(os.getenv("FLUENT_PORT", 24224))
FLUENT_TAG = os.getenv("FLUENT_TAG", "default")
class FluentHandler(logging.Handler):
"""Fluent-bit日志处理器"""
def emit(self, record):
try:
log_data = {
"level": record.levelname,
"message": record.message,
"module": record.module,
"line": record.lineno,
"topic": FLUENT_TAG,
}
event.Event(record.levelname.lower(), log_data)
except Exception as e:
print(f"Fluent日志发送失败: {str(e)}")
def setup_logger():
logger = logging.getLogger("app_logger")
logger.setLevel(logging.DEBUG)
logger.propagate = False # 防止日志重复输出
# 日志格式
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(module)s:%(lineno)d - %(message)s")
# 控制台处理器(所有环境启用)
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
console_handler.setLevel(logging.DEBUG)
logger.addHandler(console_handler)
# 输出到fluent-bit
sender.setup(FLUENT_TAG, host=FLUENT_HOST, port=FLUENT_PORT)
fluent_handler = FluentHandler()
fluent_handler.setFormatter(formatter)
fluent_handler.setLevel(logging.INFO)
logger.addHandler(fluent_handler)
return logger
# 全局日志实例
logger = setup_logger()
3.4 Unity
3.4.1 日志配置
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System;
public class FluentBitLogger : MonoBehaviour
{
private string url = "http://ip:9880";
void OnEnable() => Application.logMessageReceived += HandleLog;
void OnDisable() => Application.logMessageReceived -= HandleLog;
void HandleLog(string logString, string stackTrace, LogType type)
{
// 1. 准备数据
var logData = new LogData
{
topic = "default",
message = logString,
logType = type.ToString(),
timestamp = DateTime.Now.ToString("o")
};
// 2. 转 JSON 并发送
string json = JsonUtility.ToJson(logData);
StartCoroutine(Send(json));
}
IEnumerator Send(string json)
{
// 快速判断设备是否连接到互联网
if (Application.internetReachability == NetworkReachability.NotReachable)
{
yield break;
}
using (UnityWebRequest req = UnityWebRequest.Put(url, json))
{
req.method = "POST";
req.timeout = 1;
req.SetRequestHeader("Content-Type", "application/json");
yield return req.SendWebRequest();
}
}
[Serializable]
public class LogData
{
public string topic;
public string message;
public string logType;
public string timestamp;
}
}
3.4.2 配置插件
在FluentBit中增加http插件:
[INPUT]
Name http
Listen 0.0.0.0
Port 9880
Tag unity.logs
::: tip docker-compose文件中也要增加9880端口映射。 :::
3.5 Vue
发送HTTP数据到FluentBit中:
export const sendError = (err, instance, info) => {
try {
const log = {
top: 'vue-ui',
level: 'ERROR',
info,
component: instance?.$options?.name || 'Anonymous',
message: err.message,
url: location.href
}
fetch('http://ip:9880', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(log)
})
} catch {
}
}
利用app.config.errorHandler监听异常:
app.config.errorHandler = (err, instance, info) => {
sendError(err, instance, info)
}
四、简单使用
3.1 配置数据流
打开控制台,选择数据流->添加数据流,输入fluent bit配置文件中的流名称,选择数据流类型为Logs。
在新添加的数据流中,点击查看,跳转到日志页面即可看到上次的日志信息。
如果没有日志请检查FluentBit容器日志是否有错误,正确的应该会有上传成功的提示。

3.2 配置邮件告警
首先需要配置模板和邮箱地址。
修改docker-compose.yml文件,添加邮件服务器:
openobserve:
image: public.ecr.aws/zinclabs/openobserve:v0.14.0
container_name: openobserve
restart: unless-stopped
volumes:
- ./openobserve-data:/data
environment:
- ZO_ROOT_USER_EMAIL=njcxx0822@163.com
- ZO_ROOT_USER_PASSWORD=yourpassword
- TZ=Asia/Shanghai
- ZO_SMTP_ENABLED=true
- ZO_SMTP_HOST=smtp.163.com
- ZO_SMTP_PORT=25
- ZO_SMTP_USER_NAME=njcxx0822@163.com
- ZO_SMTP_PASSWORD=NBZzx6jXtZ2G6wrB
- ZO_SMTP_FROM_EMAIL=njcxx0822@163.com
#- ZO_SMTP_ENCRYPTION=starttls
ports:
- "5080:5080"
networks:
- devops
这里配置587端口会有问题,使用了25端口,并且去掉了安全认证。
注意配置的邮件地址需要和用户名一致。
打开控制台,选择右上角的设置按钮,选择模板->添加模板->Email,填写标题和Body:
[Alert: {alert_name}] - Severity: {alert_type}
[
{
"labels": {
"alertname": "{alert_name}",
"stream": "{stream_name}",
"organization": "{org_name}",
"alerttype": "{alert_type}",
"severity": "critical"
},
"annotations": {
"timestamp": "{timestamp}"
}
}
]
选择地址->添加地址->Email,选择刚才的模板,并填写收件人。

配置完模板和地址后,选择告警->添加告警,例如可以选择当有level='critical',并且出现1次时,触发报警,并通知到指定邮箱。

周期:监控数据的时间窗口范围,即每次检查告警条件时,回溯多长时间的监控数据。如果周期 = 5分钟:每次检查时,分析最近 5 分钟的日志/指标数据。
频率:告警规则检查的时间间隔,即每隔多久执行一次告警条件判断。
通知后静默:触发告警并发送通知后,系统暂时停止重复通知的时间。
也可以设置为实时告警,即当告警发生时就立即触发,不会有周期和频率参数设置。
3.3 配置WebHook
首先配置模板和WebHook地址。
打开控制台,选择右上角的设置按钮,选择模板->添加模板->Web Hook,填写Body:
{
"msgtype": "markdown",
"markdown": {
"content": "alertname: {alert_name} timestamp:{timestamp}"
}
}
选择地址->添加地址->WebHook,选择刚才的模板,并填写URL。
这里可以使用企业微信的群消息推送功能,添加成功后,会提供一个WebHook地址。也可以自定义一个WebHook接收地址。
配置完模板和地址后,选择告警->添加告警,选择通知到刚才的WebHook地址即可。
3.4 通过API获取日志信息
通过http://ip:5080/swagger/index.html#/ 即可访问OpenobserveAPI接口。
例如要获取日志,可以使用/api/{origin_id}/_search的接口:
curl -X 'POST' \
'http://127.0.0.1:5080/api/default/_search' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"query": {
"end_time": 1759115401962000,
"from": 0,
"size": 10,
"sql": "select * from default",
"start_time": 1759029001962000
}
}'
注意这里要加上Basic auth认证,并提供username和password。

五、自定义日志告警服务
基本原理为通过提供一个HTTP的API接口,当Openobserver触发告警时,调用该接口,并传入当前的alert_name和timestamp,然后根据timestamp和Openobserver提供的API接口去查询最近一段时间内的所有日志信息,并存储到SqLite数据库中。同时也可以选择是否同步推送到企业微信和邮箱。
最后也需要提供一个查询历史日志告警信息的接口,并可视化显示,方便运维和开发查看。
自定义服务参考代码:log-alert-service

