HelloCoder HelloCoder
首页
《Java小白求职之路》
《小白学Java》
计算机毕设
  • 一些免费计算机资源
  • 脚手架工具
  • 《从0到1学习Java多线程》
  • 《从0到1搭建服务器》
  • 《可观测和监控》
随笔
关于作者
首页
《Java小白求职之路》
《小白学Java》
计算机毕设
  • 一些免费计算机资源
  • 脚手架工具
  • 《从0到1学习Java多线程》
  • 《从0到1搭建服务器》
  • 《可观测和监控》
随笔
关于作者
  • 《从0到1学习Java多线程》

  • 《从0到1搭建服务器》

  • 可观测和监控

    • 可观测是什么
    • 指标
    • 链路
    • 日志
    • Zabbix vs Prometheus
    • 基于Micrometer的Prometheus指标生成
    • OpenTelemetry
    • Opentelemetry Collector
    • Opentelemetry尾采样
    • 基于Opentelemetry的filelog插件收集日志
    • Python接入Opentelemetry
    • 基于javaagent探针自动埋点
    • 可观测系统在Flyme落地
  • 玩转IDEA

  • 03-RPC

  • 04-Spring源码

  • 05-《Java日志框架》

  • 专栏
  • 可观测和监控
#Python #Opentelemetry #接入
码农阿雨
2025-09-05
目录

Python接入Opentelemetry

作者我平时不只是写Java,对Python也情有独钟,我尝试使用Python封装opentelemetry,然后提供一个 sdk 给公司团队使用。以下是一些思路和研究,附带到该专栏,对Python不感兴趣的可以略过。

官方对Python接入opentelemetry的说明:

https://opentelemetry.io/docs/languages/python/

前面提到,我已经把 opentelemetry-collector 已经部署起来了,并定义了一个 otlp 协议的 grpc receiver。

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317

这里我将直接把数据发送到 http://172.16.187.55:4317

# 指标、链路

其中指标Metrics、链路Trace 官方都有详细的介绍了。

只需要简单几行代码即可接入成功,然后封装一些指标api,即可打包成 sdk 。

整合Metrics、Trace 的核心代码如下:

# metrics
from opentelemetry import metrics
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter as OTLPMetricExporterGRPC
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter as OTLPTraceExporterGRPC
from opentelemetry.metrics import NoOpMeterProvider
from opentelemetry.sdk.metrics import *
from opentelemetry.sdk.metrics._internal.aggregation import AggregationTemporality
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk.metrics.view import *
from opentelemetry.sdk.metrics.view import View
from opentelemetry.sdk.resources import *
from opentelemetry.sdk.trace import TracerProvider, Tracer
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.trace import NoOpTracerProvider
# log
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter as OTLPLogExporterGRPC
from opentelemetry import _logs
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs._internal.export import BatchLogRecordProcessor

# begin
#endpoint 是 OTLP exporters grpc 协议的 receivers
endpoint = "http://172.16.187.55:4317"
metrics_exporter = OTLPMetricExporterGRPC(endpoint=endpoint)

reader = PeriodicExportingMetricReader(exporter=metrics_exporter, export_interval_millis=15,
                                       export_timeout_millis=5)

# 创建 Prometheus 导出器
# Start Prometheus client

# start_http_server(port=8073, addr="localhost")
# reader = PrometheusMetricReader()
config = get_fcop_config()
metrics_attributes = {
    SERVICE_NAMESPACE: config.tenant_code,
    SERVICE_NAME: config.app_namespace,
    SERVICE_INSTANCE_ID: config.application_code,
    CLUSTER_TYPE: config.cluster_type,
    REGION_CODE: config.region_code,
    HOST_NAME: Utils.get_hostname()
}
metrics_attributes.update(CommonTag.get_common_tag())
resource = Resource.create(metrics_attributes)
# metrics
meter_provider = MeterProvider(
    resource=resource, metric_readers=[reader]
    , views=[
        change_bucket_view
    ]
)
metrics.set_meter_provider(meter_provider)
global _meter
_meter = metrics.get_meter(name="meter-sdk")

# trace
trace_exporter = OTLPTraceExporterGRPC(endpoint=endpoint, timeout=10,
                                       compression=CompressionAlgorithm.gzip
                                      )

trace_processor = BatchSpanProcessor(span_exporter=trace_exporter,
                                     max_queue_size=5120,
                                     max_export_batch_size=1024,
                                     schedule_delay_millis=2000,
                                     export_timeout_millis=10000)
trace_provider = TracerProvider(resource=resource)
trace_provider.add_span_processor(trace_processor)
trace.set_tracer_provider(trace_provider)
global _tracer
_tracer = trace.get_tracer(instrumenting_module_name="tracer-sdk")

# 日志

日志怎么办?

这里opentelemetry也提供了一种通用的解决方案,就是类似于 filebeat那种,直接扫描指定的日志路径,然后收集日志。这里可以参考我的另外一篇文章。

以上是一种解决方案。

但是如果要通过日志拦截发送的方式,可能会是更好的方案。

后来我看了下opentelemetry-python (opens new window)的仓库,它的介绍是有关于 Log的试验的:

Signal Status Project
Traces Stable N/A
Metrics Stable N/A
Logs Experimental N/A

官方也说明了,日志目前还是 开发 中。

2025年8月21日 18:08:56。此时我使用的是 v136.0 版本,注意这里的版本要求

1.34.0 Requires-Python >=3.9; 1.34.1 Requires-Python >=3.9; 1.35.0 Requires-Python >=3.9; 1.36.0 Requires-Python >=3.9

那这就尴尬,指标、链路都有数据了,没有日志可不行。好消息是最近看到它给了一个例子:

https://opentelemetry.io/docs/languages/python/instrumentation/#logs

接着看,大家猜我发现了什么?

仓库的代码,它在一个example (opens new window)的使用例子里面,有关于log的使用demo:

安装我本地试了下,竟然成功了。

这样日志直接以otel的协议就发送给 otel-collector 了,这种方案就更简单了。

代码:

otel_sdk_config.py :

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    :  2025/8/21 18:11
# @Author  : 
# @File    : otel_sdk_config
import logging
import os
from logging.handlers import RotatingFileHandler
from pathlib import Path

from opentelemetry._logs import set_logger_provider
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.sdk.resources import Resource

from common.singleton import SingletonMeta

OTEL_EXPORTER_ENDPOINT = "OTEL_EXPORTER_ENDPOINT"


def setup_custom_logging():
    """配置自定义日志系统,输出到控制台和文件"""

    # 获取当前工作目录(项目根目录)
    log_dir = Path.cwd()

    # 创建根日志记录器
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)  # 设置最低日志级别

    # 清除现有的处理器(避免重复添加)
    logger.handlers.clear()

    # 创建格式化器
    formatter = logging.Formatter(
        '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )

    # 1. 控制台处理器
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)  # 控制台只显示 INFO 及以上级别
    console_handler.setFormatter(formatter)

    # 2. 文件处理器 - 按文件大小轮转
    file_handler = logging.handlers.RotatingFileHandler(
        filename=log_dir / "app.log",
        maxBytes=10 * 1024 * 1024,  # 10MB
        backupCount=5,  # 保留5个备份文件
        encoding='utf-8'
    )
    file_handler.setLevel(logging.DEBUG)  # 文件记录所有级别
    file_handler.setFormatter(formatter)

    # 添加处理器到日志记录器
    logger.addHandler(console_handler)
    logger.addHandler(file_handler)

    return logger


class OtelConfig(metaclass=SingletonMeta):

    def __init__(self):
        endpoint = os.getenv(OTEL_EXPORTER_ENDPOINT) or os.getenv(
            OTEL_EXPORTER_ENDPOINT.lower()) or os.getenv(
            OTEL_EXPORTER_ENDPOINT.lower().replace('_', '.')) or "http://localhost:4317"

        exporter = OTLPLogExporter(endpoint)
        record_processor = BatchLogRecordProcessor(exporter)

        log_provider = LoggerProvider(
            resource=Resource.create(
                {
                    "service.name": "business-demo-web",
                    "service.instance.id": "instance-12",
                }
            ),
        )
        log_provider.add_log_record_processor(record_processor)
        set_logger_provider(log_provider)
        handler = LoggingHandler(level=logging.INFO, logger_provider=log_provider)
		# 自定义 logger 配置
        logger = setup_custom_logging()
        logger.addHandler(handler)

        logging.info(" OTEL config successfully")

otel_logs.py main 测试类入口:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    :  2025/8/21 18:03
# @Author  : 
# @File    : otel_logs
import os
import logging
import threading
from datetime import datetime

from com.opentelemetry.otel_sdk_config import OtelConfig, get_loggers

os.environ["OTEL_EXPORTER_ENDPOINT"] = "http://172.16.187.55:4317"

if __name__ == '__main__':

    config = OtelConfig()
    print("OpenTelemetry")

    extra_attributes = {
        "thread.id": threading.get_ident(),
        "thread.name": threading.current_thread().name,
        "process.id": os.getpid(),
        "timestamp": datetime.now().isoformat(),
        "custom.field": "custom_value"
    }

    logger = logging.getLogger(__name__)
    logger.info("测试 otel logger 上报")

可以看到,日志已经上报成功:

关于 opentelemetry log 模块的使用,仅支持 python 的 logging 日志库,像其他的日志框架和库如:loguru、structlog、logbook 均不支持日志拦截上报。

阅读全文
×

(为防止恶意爬虫)
扫码或搜索:HelloCoder
发送:290992
即可永久解锁本站全部文章

解锁
#Python#Opentelemetry#接入
上次更新: 2025-09-05 07:09:03
最近更新
01
阿里面试题(答案)
09-05
02
《LeetCode 101》
09-05
03
《LeetCode CookBook》
09-05
更多文章>
Theme by Vdoing | Copyright © 2020-2025 码农阿雨
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式