Skip to content

feat: 【RUM】Span 列表页接口 --story=137033151 - #11887

Open
joker-joker-yuan wants to merge 7 commits into
TencentBlueKing:masterfrom
joker-joker-yuan:feat/rum_span_list_api/#1010158081137033151
Open

feat: 【RUM】Span 列表页接口 --story=137033151#11887
joker-joker-yuan wants to merge 7 commits into
TencentBlueKing:masterfrom
joker-joker-yuan:feat/rum_span_list_api/#1010158081137033151

Conversation

@joker-joker-yuan

Copy link
Copy Markdown
Collaborator

No description provided.

@joker-joker-yuan joker-joker-yuan added feat A new feature. Correlates with MINOR in SemVer project/apm project apm labels Aug 11, 2026
@joker-joker-yuan
joker-joker-yuan force-pushed the feat/rum_span_list_api/#1010158081137033151 branch from 97a44da to 3a946d4 Compare August 13, 2026 02:51
@joker-joker-yuan
joker-joker-yuan force-pushed the feat/rum_span_list_api/#1010158081137033151 branch from 3a946d4 to 92cd884 Compare August 13, 2026 02:52
Comment thread bkmonitor/packages/rum_web/docs/api/search.md Outdated
Comment thread bkmonitor/packages/rum_web/handlers/level/span.py Outdated
Comment thread bkmonitor/bkmonitor/utils/elasticsearch/handler.py Outdated
Comment thread bkmonitor/packages/rum_web/query/resources.py Outdated
Comment thread bkmonitor/packages/rum_web/handlers/level/span.py Outdated

@ZhuoZhuoCrayon ZhuoZhuoCrayon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

本轮先评论合入安全和列表协议问题;序列化器继承会在对话里单独给方案,不在这次 review 里展开。

需要改:

  1. generate_query_string 与 APM 一样不鉴权,避免缺 app_name 时 500。
  2. view_config 经 Factory 按 mode 分派,返回单个 mode 的核心协议,而不是 {span_config, view_config, session_config}
  3. generate_query_string 下沉到 Level,Resource 经 Factory 调用。

Comment thread bkmonitor/packages/rum_web/query/views.py
application = _get_authorized_application(data["bk_biz_id"], data["app_name"])
span_handler = RumLevelHandlerFactory.create(RumQueryMode.SPAN.value, _build_data_sources([application]))
return {
"span_config": span_handler.view_config(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bkmonitor/packages/rum_web/query/resources.py:80 [P1]

"span_config": span_handler.view_config(

问题:方案约定 mode 选择 Level,Resource 不选择具体 Level 类,view_config 返回当前 mode 的单份配置。当前写死 SPAN,再拼 {span_config, view_config: {}, session_config: {}},工厂没有分派,空 dict 还会泄漏未就绪层级。

建议:按核心协议返回单个 mode 的 view_config

handler = RumLevelHandlerFactory.create(data["mode"], _build_data_sources([application]))
return handler.view_config(
    start_time=data["start_time"],
    end_time=data["end_time"],
)

Comment thread bkmonitor/packages/rum_web/handlers/level/span.py Outdated
Comment thread bkmonitor/packages/rum_web/handlers/level/base.py
Comment thread bkmonitor/packages/rum_web/query/serializers.py
Comment thread bkmonitor/packages/rum_web/query/resources.py Outdated

@ZhuoZhuoCrayon ZhuoZhuoCrayon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

本轮先评论未注册 mode 的 500、文档与实现不一致、以及鉴权白名单;view_config 字段组装单独在对话里确认后再评。

from rum_web.handlers.level.span import SpanLevelHandler


class UnsupportedRumQueryMode(ValueError):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bkmonitor/packages/rum_web/handlers/level/factory.py:17 [P1]

class UnsupportedRumQueryMode(ValueError):

问题:RumQueryMode 已经包含 view / session,序列化器会放行。Factory 首期只注册 span,未注册模式走 ValueError,落到 custom_exception_handler() 的兜底分支:HTTP 500 + ERROR 栈。文档把 view / session 写成合法枚举,联调时踩得到。

建议:把这个异常改成 DRF 能识别的 400。枚举可以保留三档,未注册交给 Factory 拒绝。

from django.utils.translation import gettext_lazy as _
from rest_framework.exceptions import ValidationError


class UnsupportedRumQueryMode(ValidationError):
    """未注册的 RUM 查询模式"""

    def __init__(self, mode: str):
        super().__init__(_("不支持的 RUM 查询模式: {}").format(mode))

}
```

#### 2.1.2 Response

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bkmonitor/packages/rum_web/docs/api/search.md:71 [P1]

返回包含 totaldata 字段的分页结构。

问题:实现已经按首轮结论改了,文档没跟上。前端按这份协议接会接错。

接口 文档仍写 当前实现
list_records {total, data: {list}} {list: [...]},没有 total
view_config {span_config, view_config, session_config} 单个 mode 的字段元数据
generate_query_string 只有 filters 还要 bk_biz_id / app_name / mode

建议:list_records 响应改成:

{
  "list": [
    {
      "span_id": "29926da51cae17cf",
      "attributes.span_type": "resource",
      "status.code": 0
    }
  ]
}

view_config 示例去掉 span_config 那一层,顶层直接给 default_sort / fields / groups / display_fieldsgenerate_query_string 补上应用上下文。


INSTANCE_ID = "app_name"

def get_permissions(self):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bkmonitor/packages/rum_web/query/views.py:28 [P2]

def get_permissions(self):

问题:generate_query_string 已经不鉴权,这点对。当前是数据接口白名单,未列入的 action 默认 []。里程碑 4 加上 field_topk 时,漏改这一处就会裸奔。

建议改成「除查询串转换外都鉴权」:

def get_permissions(self):
    if self.action == "generate_query_string":
        return []
    return [
        InstanceActionForDataPermission(
            self.INSTANCE_ID,
            [ActionEnum.VIEW_RUM_APPLICATION],
            ResourceEnum.RUM_APPLICATION,
            get_instance_id=Application.get_application_id_by_app_name,
        )
    ]

@ZhuoZhuoCrayon ZhuoZhuoCrayon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

view_config 字段组装:fields 写两遍、WEB_VITALS 空分组、display_fields 用了 user.id。

config_dict = {
"default_sort": copy.deepcopy(self.query.DEFAULT_SORT),
"fields": [
{

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bkmonitor/packages/rum_web/handlers/level/span.py:68 [P1]

"fields": [

问题:view_config 现在会交出三份错协议。

  1. fields 写了两遍。这里先用列表推导填一遍,后面循环里再 append 一遍。all_fields 有 N 条,fields 就有 2N 条;前 N 条还没有 unit / option_values
config_dict = {
    "default_sort": copy.deepcopy(self.query.DEFAULT_SORT),
    "fields": [
        {
            "name": field_name,
            "alias": field_dict.get("alias_name"),
            "type": field_dict.get("field_type"),
            "is_searchable": field_dict.get("is_searchable", False),
            "is_agg": field_dict.get("is_agg", False),
            "is_list": field_dict.get("is_list", True),
            "supported_operations": field_dict.get("supported_operations", []),
        }
        for field_name, field_dict in all_fields.items()
    ],
    ...
}

for field_name, field_dict in all_fields.items():
    _field_dict = {
        "name": field_name,
        "alias": field_dict["alias_name"],
        "type": field_dict["field_type"],
        "is_searchable": field_dict["is_searchable"],
        "is_agg": field_dict["is_agg"],
        "is_list": field_dict["is_list"],
        "supported_operations": field_dict["supported_operations"],
    }
    if "unit" in field_dict:
        _field_dict["unit"] = field_dict["unit"]
    if "option_values" in field_dict:
        _field_dict["option_values"] = field_dict["option_values"]
    field_map[field_name] = _field_dict
    config_dict["fields"].append(_field_dict)
  1. WEB_VITALS 分组是空的。白名单写了 LCPCLSINPFCPTTFB,这些名字不在 query_fields() 的 mapping 里。field_map 只来自 mapping,组装分组时找不到就跳过。

  2. DISPLAY_FIELDS 用了 user.id。协议和 USER 分组都是 attributes.user.id,默认表头会空列。

建议:删掉第一次列表推导,fields 只保留循环里那一次;虚拟字段在组分组之前补进 field_map;默认列改成 attributes.user.id

all_fields = self.query.query_fields(start_time, end_time)

field_map: dict[str, dict[str, Any]] = {}
for field_name, field_dict in all_fields.items():
    item = {
        "name": field_name,
        "alias": field_dict["alias_name"],
        "type": field_dict["field_type"],
        "is_searchable": field_dict["is_searchable"],
        "is_agg": field_dict["is_agg"],
        "is_list": field_dict["is_list"],
        "supported_operations": field_dict["supported_operations"],
    }
    if "unit" in field_dict:
        item["unit"] = field_dict["unit"]
    if "option_values" in field_dict:
        item["option_values"] = field_dict["option_values"]
    field_map[field_name] = item

# mapping 没有的虚拟字段,先补进 field_map,WEB_VITALS 才组得起来
for name, meta in self.VIRTUAL_FIELDS.items():
    field_map.setdefault(name, meta)

return {
    "default_sort": list(self.query.DEFAULT_SORT),
    "fields": list(field_map.values()),
    "groups": [
        {
            "name": group["name"],
            "alias": group["alias"],
            "fields": [field_map[name] for name in group["field_names"] if name in field_map],
        }
        for group in RUM_SEARCH_PAGE_GROUPS.get("span", [])
    ],
    "display_fields": list(self.DISPLAY_FIELDS),
}

VIRTUAL_FIELDS 放在 SpanLevelHandler 上,把 LCP 等虚拟字段的 name / alias / type / unit / is_list 写死。DISPLAY_FIELDSuser.id 改成 attributes.user.id

],
},
{
"name": "INTERACTION",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACTION


field_map: dict[str, dict[str, Any]] = {}
for field_name, field_dict in all_fields.items():
_field_dict = {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bkmonitor/packages/rum_web/handlers/level/span.py:85 [P1]

for field_name, field_dict in all_fields.items():

问题:BaseQuery._query_fields() 已经给出字段对象,Level 又映射成 name / alias / type / unit。两边命名不一致,每层都要精修一遍。

建议:查询层和接口层用同一套字段名。BaseQueryalias_name 改成 field_aliasunit 改成 field_unitview_config 沿用:

field_name
field_alias
field_type
field_unit
origin_field
is_searchable
is_agg
is_list
supported_operations

Level 只丢掉查询层私有键,不再手写字段字典:

ignore_keys: list[str] = ["is_case_sensitive", "is_analyzed"]
for key in ignore_keys:
    field_dict.pop(key, None)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat A new feature. Correlates with MINOR in SemVer project/apm project apm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants