Repository navigation
Conversation
Copy the configured base URL before adding per-request variables so concurrent Metric, Trace and Log requests cannot modify shared configuration. Include unchanged-base and concurrent-request regression tests.
Size the iframe against its Admin container and use Grafana kiosk mode instead of fixed offsets and cross-origin DOM manipulation. Keep dashboard URL variables and existing kiosk settings intact.
Add Application and Instance Log dashboard URL APIs plus exact Jaeger trace start-time lookup and bounded trace-focused log URLs. Include configuration, request models, routes, validation and service tests; Admin does not query the log backend directly.
Add Application and Instance Loki dashboards with five-column log tables and structured/raw Trace ID filtering. Mount their ConfigMaps, configure Admin log URLs and the Jaeger query endpoint, and document the external logging field contract without deploying a log backend.
Add Application and Instance Log tabs and a shared Trace ID search form. Resolve the exact trace start through Jaeger, use T +/- five minutes, distinguish lookup and dashboard errors, and prevent the initial dashboard request from overwriting a search result. Include routes, API clients, translations and regression tests.
Expose applicationRaw on Application and Instance Log URLs while preserving existing application escaping and bare-IP instance identity. Add ordinary and dotted application-name tests and keep manual Trace ID parameters unchanged.
Add Application and Instance Elasticsearch dashboards using exact applicationRaw, pod_ip and trace_id keyword filters without Body trace scanning. Include stable five-column transformations, ConfigMap mounts, optional Admin URL examples and the raw-mapping integration contract; the environment supplies the datasource and Elasticsearch runtime.
Document the shared log field contract and provide reference OTel Collector configurations for Loki and Elasticsearch. Explain Pod metadata enrichment, raw Body preservation, permissions, datasource setup and verification without installing a production logging pipeline.
robocanic
left a comment
There was a problem hiding this comment.
Thanks for the contribution — the connect-only design is clean, CI is green, and Go/frontend tests are included. Approving with a few comments; items 1–2 are worth addressing before merge.
Good
- Added the missing
returnafter thegrafanaDashboards == nilcheck inGetGrafanaDashboard. concatURLWithQueryVarsnow copies the*url.URLinstead of mutating the shared config URL (real bug fix, covered by a concurrency test).- Trace ID is validated on both backend and frontend; the 10-minute range cap matches the frontend T±5min.
Suggestions
- Trace ID comparison (
service/trace.go):EqualFold(result.Data[0].TraceID, traceID)can false-positive as "different trace id". Jaeger v1 returns a 16-char ID when the high 64 bits are 0, and Jaeger v2 may zero-pad a 16-char ID to 32. Please normalize (trim or pad leading zeros) before comparing, and add a test. - Unbounded response body:
io.ReadAll(resp.Body)has no limit; please useio.LimitReader. Also consider reusing a package-levelhttp.Clientinstead of creating one per request. var-applicationescaping is inconsistent: Application log sends a regex-quoted value (used with${application:raw}), while Instance log sends the raw name (used with${application:regex}). Both work, but the contract is implicit; please document it or unify.GrafanaDashboardConfig.Traceis only meaningful forlogDashboards, yet it lives in the shared struct, and the default config points it at the same URL asapplication. Consider warning inValidate()for non-log configs, or reusingApplicationfor trace mode.GrafanaPage.vuerewrite affects existing Metric/Trace pages (iframe DOM manipulation removed,kiosk=1forced when absent). Direction is fine, but please call it out in the PR description, ideally split it out or add regression screenshots.- ES queries:
app.keyword:"${applicationRaw:raw}"breaks if the app name contains"or\; consider escaping in the backend or documenting the limitation. In trace modevar-applicationRawis not sent, so the query relies on theOR trace_idbranch — a short comment would help. - Nits:
ui-vue3/src/api/service/trace.tslacks the ASF license header;app/dubbo-admin/dubbo-admin.yamldoesn't get the newjaeger/logDashboardsexamples; frontend error messages are hard-coded English although i18n entries were added; thebaseURL == nilcheck for the trace log dashboard is duplicated in handler and service.
robocanic
left a comment
There was a problem hiding this comment.
审查意见
整体实现比较完整:新路由都挂在 protected 下;GetTraceStartTime 只接受 16/32 位十六进制 Trace ID 且 Jaeger 地址来自配置,没有 SSRF 风险;concatURLWithQueryVars 改为复制 url.URL 避免修改共享配置,GetGrafanaDashboard 补上缺失的 return 也修复了配置缺失时的空指针。我在 PR head 上跑了 go test ./pkg/console/service/... ./pkg/config/...,均通过。以下是几点需要关注的问题:
1. Trace ID 大小写未规范化,ES 面板会查不到日志
pkg/console/service/trace.go(GetTraceStartTime返回的TraceStartTimeResp.TraceID)与pkg/console/service/observability.go(GetTraceLogDashboard的var-traceId)都原样透传用户输入的大小写;前端又用后端返回的traceId构造请求。- Loki 面板使用
(?i)做不区分大小写匹配,而 ES 面板使用trace_id.keyword:"${traceId:raw}",.keyword是区分大小写的精确匹配。用户粘贴大写形式的 Trace ID(traceIDPattern与前端isValidTraceId都允许)时,Jaeger 能查到、Loki 能查到,但 ES 日志页会静默返回空结果,且没有任何错误提示。 - 建议:在
GetTraceStartTime与GetTraceLogDashboard中统一strings.ToLower后再返回 / 拼接 URL,并在trace_test.go、observability_test.go中补一个大写输入的用例。
2. handler 层改动缺少测试
pkg/console/handler/observability.go:新增的LogDashboard分支(应用 / 实例走GetApplicationLogDashboard/GetInstanceLogDashboard)、GetTraceStartTime、GetTraceLogDashboard,以及配置缺失时新增的return(此前会继续往下执行并解引用 nil 的grafanaDashboards)都没有 handler 级测试。PR 描述中“Relevant Go tests inservice,handler, andconfig/console”与实际不符——本 PR 没有改动任何pkg/console/handler/*_test.go,该包现有测试(auth_test.go、rule_version_test.go)也与这些改动无关。- 建议至少补:
logDashboards未配置时返回NotFoundError且不 panic;/trace/log-dashboard在logDashboards.trace未配置时的行为。
明白 |
Resolve observability conflicts while preserving upstream instance trace identity and log dashboard functionality. Share instance lookup and retain both metric/trace and log regression tests.
|
AlexStocks
left a comment
There was a problem hiding this comment.
结论:可以合入,修掉一处数据竞争是本 PR 的额外收益
37 个文件,changed_files=37 与 Diff 一致;Go 侧改动集中在可观测性配置与 dashboard URL 生成,UI 侧新增日志页签与 Trace ID 搜索。
做得好的地方
concatURLWithQueryVars现在复制*url.URL再改RawQuery。原实现直接修改配置里的baseURL,在并发请求下是对共享配置对象的写竞争;这个修复虽然没有写在 PR 标题里,但价值很高。GetGrafanaDashboard在grafanaDashboards == nil之后补了return。原代码只写了错误响应却继续往下走,会拿 nil 指针继续拼 URL。GetTraceStartTime的安全边界比较完整:Trace ID 限定 16/32 位十六进制、Jaeger 响应限制 16 MiB、客户端 5s 超时、校验返回的 Trace ID 与请求一致、earliest/1000正确完成微秒到毫秒的换算。fmt.Sprintf("%032s", ...)的补零对比我单独验证过,确实按零填充([%032s]→000…abc123),16 位与 32 位 ID 的等价判断成立。
已核对的转义设计
GetApplicationLogDashboard 对应用名做 regexp.QuoteMeta 后再 strconv.Quote 并去掉外层引号,结果是 shop.user → shop\.user(经过 JSON 解码后是 shop\\.user)。这个双重转义不是 bug:仪表盘里 ${application:raw} 位于 LogQL 双引号字符串内,字符串层先消费一个反斜杠,正则层再消费第二个,最终匹配字面量点号。文档里对这条契约写得很清楚,测试也断言了 shop\\.user。
P3 · Trace 搜索只有单元测试
GetTraceStartTime / GetTraceLogDashboard 的测试走 httptest,覆盖了状态码、超大响应、Trace ID 不匹配等分支,这部分很好;但没有对着真实 Jaeger 的 16 位 ID 返回做一次验证。文档里提到的「zero-padded 32 位与 16 位等价」属于 Jaeger 侧行为,建议在验收记录里补一条真实查询结果。
P3 · GetApplicationLogDashboard 未校验空应用名
req.AppName 为空时会生成 var-application="",最终是空面板。既有 GetAppDashboard 也没有校验,属于一致行为,可以不改;如果要改,建议两个入口一起加。
P3 · 时间范围校验不检查上界
GetTraceLogDashboard 校验了 to > from 和跨度 ≤ 10 分钟,但没有拒绝明显在未来的 to。Grafana 会自行处理,影响很小,记录一下。
明白 |















Summary
Add Application / Instance log entries to Dubbo Admin, supporting logs stored in Loki or Elasticsearch through Grafana and manual Trace ID searches for related logs within the log page. Provide four log dashboards and Kubernetes automatic loading configuration, plus a field contract, Collector examples, and an ES index template reference for the external collection pipeline.
This PR uses a connect-only model: Admin owns the entries and dashboard URLs, Grafana owns log queries, and deployers supply log storage, datasources, and collectors.
Motivation
Upstream already has Metric / Trace entries but lacks corresponding Application / Instance log pages. When troubleshooting application or instance problems, users need to locate the data in an external log system instead of reusing the application and instance identity selected in Admin.
This PR adds the first-stage log-viewing capability and supports Loki and Elasticsearch through shared parameters and a field contract, avoiding two separate sets of provider-specific logic in Admin.
Changes
1. Application and Instance Log Entries
fullURL. It does not return log records, connect directly to Loki / Elasticsearch, or perform HTTP redirects.console.logDashboards, without provider checks.var-applicationbehavior and addvar-applicationRaw. Existing regex escaping for Application Log remains unchanged, while ES uses the actual raw application name for exact matching.2. Manual Trace ID Search on the Log Page
New protected GET APIs:
/api/v1/application/log-dashboard/api/v1/instance/log-dashboard/api/v1/trace/start-time/api/v1/trace/log-dashboard3. Loki / ES Dashboards and Deployment Integration
Time | Level | Pod | Trace ID | Message.appindex label andpod_ipstructured metadata. Trace IDs may come from structured fields or the log body, with complete 16- or 32-character IDs extracted.@timestamp, the originalBody, and top-level LogRecord attributes. Application, instance, and trace queries use the corresponding.keywordfields, without scanningBodyfor Trace IDs or using an ECS adapter.application,instance, andtraceURLs.4. Shared Fixes and Integration Examples
.keywordfields. It is a manual-installation example only and does not automatically create indices, alter existing indices, or overwrite production templates.Verification
Local unit tests, builds, and static checks are complete. Runtime and browser acceptance were manually verified by the author, who confirmed on 2026-10-06 that all checks below passed. These manual acceptance results are not browser automation results.
service,handler, andconfig/console;config/observabilityhas no test files--hookTimeout 60000go build ./...; relevant Go tests and builds at all eight commit checkpoints.keywordcontract, and documentation linksgit diff --checkReview Focus
ORbranch in ES queries switch correctly: the default.*for an inactive variable is not “match all” in a quoted keyword query and must not be interpreted as a Loki regex. Focus on query scope and branch-switching semantics.Compatibility / Impact
var-applicationRawis additive; existing log application parameters are unchanged. Manual trace search still uses wildcard application / instance parameters without a raw application parameter..svcexample addresses do not guarantee browser access outside the cluster.Affected areas (matching the repository PR template):
Scope
Included
Not Included
Related Issue
Fixes #1586
Dependency
Built on the Service Trace parameter and Kubernetes dashboard provisioning changes already merged into develop (#1569 and #1573). This PR does not depend on the pending Instance Trace bare-IP / Kubernetes Pod-identity PRs: instance lookup and test helpers for the log backend are self-contained.