feature: Implement Event stream for Application/Instance/Service #1472#1487
feature: Implement Event stream for Application/Instance/Service #1472#1487ambiguous-pointer wants to merge 1 commit into
Conversation
|
Please fix the CI problem @ambiguous-pointer |
…ent ingestion ([apache#1472](apache#1472)) - Add K8sEvent resource type (proto + Go spec) and store indexes - Add K8sEventListerWatcher to watch K8s /api/v1/events via client-go - Register K8sEventListerWatcher in Kubernetes EngineFactory - Add /application/event, /instance/event, /service/event console API endpoints - Add EventTimeline shared Vue component with normal/warning node styles - Wire up event tabs for application, instance, and service detail pages - Un-hide event tab routes in frontend router - Add mock event handlers for development - Add PlatformEvent resource type (platformevent_types.go) with store indexes - Add shared platform_event_recorder utility for event recording - Record ZK config change events (tag-route, condition-route, dynamic-config) - Record ZK metadata events (provider/consumer metadata added/updated) - Record Nacos instance registration/deregistration events - Record Nacos consumer metadata change events - Merge K8s events and Platform events into unified timeline in event query service - Downgrade "no subscriber" log level from INFO to DEBUG to reduce log noise
71cc134 to
769173b
Compare
|
Apologies for the formatting oversight. I have run the linter locally, fixed the Vue component styles, and force-pushed the updates. The CI check is now green. |
There was a problem hiding this comment.
Pull request overview
This PR adds an end-to-end “event timeline” feature across the backend and ui-vue3, ingesting Kubernetes Event objects and recording registry-side (ZooKeeper/Nacos) platform events, then exposing unified, time-sorted, paginated timelines for Applications / Instances / Services with frontend visualization.
Changes:
- Introduces new resource kinds (
K8sEvent,PlatformEvent) with store indexes, plus K8s lister-watcher ingestion and registry subscriber-side PlatformEvent recording. - Adds Console API endpoints (
/application/event,/instance/event,/service/event) that merge and paginate K8s + Platform events. - Adds a reusable Vue timeline component and wires event tabs + routes + i18n + mocks to display the unified event stream.
Reviewed changes
Copilot reviewed 34 out of 34 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| ui-vue3/src/views/resources/services/tabs/event.vue | Replaces placeholder timeline with EventTimeline + paginated API loading for service events |
| ui-vue3/src/views/resources/instances/tabs/event.vue | Adds EventTimeline-based instance event tab with pagination |
| ui-vue3/src/views/resources/instances/tabs/detail.vue | Adds “instance missing/offline” empty state and silences detail fetch errors |
| ui-vue3/src/views/resources/instances/slots/InstanceTabHeaderSlot.vue | Silences errors when fetching instance lifecycle state |
| ui-vue3/src/views/resources/applications/tabs/event.vue | Replaces old custom timeline UI with shared EventTimeline + pagination |
| ui-vue3/src/types/api.ts | Adds EventItem type for unified timeline entries |
| ui-vue3/src/router/defaultRoutes.ts | Enables event tabs in routes for application/instance/service |
| ui-vue3/src/mocks/handlers/service.ts | Adds mock /service/event response |
| ui-vue3/src/mocks/handlers/instance.ts | Adds mock /instance/event response |
| ui-vue3/src/mocks/handlers/app.ts | Updates mock application events to unified EventItem format with totals |
| ui-vue3/src/components/EventTimeline.vue | New shared timeline component with infinite-scroll loading |
| ui-vue3/src/base/i18n/zh.ts | Adds eventExpiryHint translation |
| ui-vue3/src/base/i18n/en.ts | Adds eventExpiryHint translation |
| ui-vue3/src/base/http/request.ts | Adds silentError support for suppressing toast/log output |
| ui-vue3/src/api/service/service.ts | Adds listServiceEvent() client |
| ui-vue3/src/api/service/instance.ts | Adds silentError option for getInstanceDetail() and adds listInstanceEvent() |
| ui-vue3/src/api/service/app.ts | Tightens listApplicationEvent() params typing (adds pagination params) |
| pkg/engine/kubernetes/listerwatcher/k8s_event.go | New watcher to list/watch core/v1 Events and transform into K8sEvent resources |
| pkg/engine/kubernetes/factory.go | Registers the new K8sEvent lister-watcher in the K8s engine |
| pkg/core/store/index/platform_event.go | Adds store indexes for PlatformEvent queries |
| pkg/core/store/index/k8s_event.go | Adds store indexes for K8sEvent queries |
| pkg/core/resource/apis/mesh/v1alpha1/platformevent_types.go | Defines PlatformEvent resource schema + list |
| pkg/core/resource/apis/mesh/v1alpha1/k8sevent_types.go | Defines K8sEvent resource schema + list |
| pkg/core/events/component.go | Downgrades “no subscriber” log from INFO to DEBUG |
| pkg/core/discovery/subscriber/zk_metadata.go | Records PlatformEvent entries for ZK metadata add/update |
| pkg/core/discovery/subscriber/zk_config.go | Records PlatformEvent entries for ZK config add/update/delete |
| pkg/core/discovery/subscriber/platform_event_recorder.go | Adds shared PlatformEvent store-write utility |
| pkg/core/discovery/subscriber/nacos_service.go | Records PlatformEvent entries for Nacos instance/consumer metadata changes |
| pkg/console/service/event.go | Implements merged K8s + Platform event querying and pagination |
| pkg/console/router/router.go | Adds /application/event, /instance/event, /service/event routes |
| pkg/console/model/event.go | Adds request/response models for event queries |
| pkg/console/handler/event.go | Adds handlers for the new event endpoints |
| api/mesh/v1alpha1/k8s_event.proto | Adds proto definition for K8sEvent spec |
| api/mesh/v1alpha1/k8s_event.go | Adds Go struct + Clone for K8sEvent spec |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| res, ok := items[i].(*K8sEventResource) | ||
| if !ok { | ||
| logger.Errorf("unexpected resource type, expected: %s, get %s", K8sEventKind, res.ResourceKind()) | ||
| continue | ||
| } |
| watch( | ||
| () => [props.hasMore, props.loading, props.loadingMore, props.events.length], | ||
| () => { | ||
| tryLoadMore() | ||
| } | ||
| ) |
| total := len(entries) | ||
| start := pageReq.PageOffset | ||
| if start > total { | ||
| start = total | ||
| } | ||
| end := start + pageReq.PageSize | ||
| if pageReq.PageSize <= 0 || end > total { | ||
| end = total | ||
| } |
| mesh := constants.DefaultMesh | ||
| res := meshresource.NewK8sEventResourceWithAttributes(k8sEvent.Namespace+"/"+k8sEvent.Name, mesh) | ||
| res.Spec = &meshproto.K8sEvent{ | ||
| Namespace: k8sEvent.Namespace, | ||
| Reason: k8sEvent.Reason, | ||
| Message: k8sEvent.Message, | ||
| Type: k8sEvent.Type, | ||
| InvolvedObjKind: k8sEvent.InvolvedObject.Kind, | ||
| InvolvedObjName: k8sEvent.InvolvedObject.Name, | ||
| SourceComponent: k8sEvent.Source.Component, | ||
| SourceHost: k8sEvent.Source.Host, | ||
| FirstTimestamp: k8sEvent.FirstTimestamp.Format(constants.TimeFormatStr), | ||
| LastTimestamp: k8sEvent.LastTimestamp.Format(constants.TimeFormatStr), | ||
| Count: k8sEvent.Count, | ||
| EventSource: "KUBERNETES", | ||
| } |
| func ListInstanceEvents(ctx consolectx.Context, req *model.EventQueryReq) (*model.EventListResp, error) { | ||
| k8sEvents, err := manager.ListByIndexes[*meshresource.K8sEventResource]( | ||
| ctx.ResourceManager(), | ||
| meshresource.K8sEventKind, | ||
| []index.IndexCondition{ | ||
| {IndexName: index.ByMeshIndex, Value: req.Mesh, Operator: index.Equals}, | ||
| }, | ||
| ) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| platformEvents, err := manager.ListByIndexes[*meshresource.PlatformEventResource]( | ||
| ctx.ResourceManager(), | ||
| meshresource.PlatformEventKind, | ||
| []index.IndexCondition{ | ||
| {IndexName: index.ByMeshIndex, Value: req.Mesh, Operator: index.Equals}, | ||
| }, | ||
| ) | ||
| if err != nil { | ||
| return nil, err | ||
| } |
robocanic
left a comment
There was a problem hiding this comment.
Great Work! Thanks a lot for your PR! I've reviewed the PR generally and the whole structrue is totally fine. There are some issues both I and Qoder found, please check the review and find them if make sense.
completeness-review.md
correctness-review.md
impact-review.md
|
|
||
| import "api/mesh/options.proto"; | ||
|
|
||
| message K8sEvent { |
There was a problem hiding this comment.
Question: PlatformEvent为什么没有proto定义?
| } | ||
|
|
||
| func buildApplicationK8sConditions(req *model.EventQueryReq) []index.IndexCondition { | ||
| conditions := []index.IndexCondition{ |
There was a problem hiding this comment.
Correction: 这里的AppName参数需要前端必传,不传需要抛异常,不然这里的过滤条件太宽泛了,会把整个mesh的所有事件全部捞出来。下面的过滤条件也是一样



This change introduces a complete event timeline feature for applications, instances, and services
based on discussion #1474 and
issue #1472.
It ingests Kubernetes events via the cluster API and records platform events from ZooKeeper
and Nacos registration centers, merging them into a unified, time-sorted timeline with full
frontend visualization.
📌 What's New
Kubernetes Event Ingestion
K8sEvent resource type
K8sEventResourcewith protoK8sEventspec (api/mesh/v1alpha1/k8s_event.proto+Go generated code). Captures namespace, reason, message, type, involved object kind/name,
source component/host, first/last timestamps, count, and a static
EventSource: "KUBERNETES"marker.K8sEventListerWatcher
New
K8sEventListerWatcher(pkg/engine/kubernetes/listerwatcher/k8s_event.go) watchesapi/v1/eventsacross all namespaces usingclient-go'sListWatch+TransformFunc.Registered in the K8s
EngineFactoryalongside the existingPodListerWatcher.K8sEvent store indexes (
pkg/core/store/index/k8s_event.go)ByK8sEventInvolvedObjKind,ByK8sEventInvolvedObjName,ByK8sEventType,ByK8sEventSource— enables efficient queries by mesh, involved object, event type, and source.
Platform Event Recording (ZooKeeper & Nacos)
PlatformEvent resource type
PlatformEventResourcewithPlatformEventspec (pkg/core/resource/apis/mesh/v1alpha1/platformevent_types.go).Carries
AppName,InstanceName,InstanceIP,ServiceName,Type(normal/warning),Message,Source(Zookeeper/Nacos),SourceType(provider/consumer/config),Category,Action, andEventTime.Shared platform event recorder (
pkg/core/discovery/subscriber/platform_event_recorder.go)recordPlatformEvent()utility that createsPlatformEventResourceinstances and persiststhem directly to the store via
storeRouter.ResourceKindRoute(PlatformEventKind).ZooKeeper config change events
ZKConfigEventSubscriber(pkg/core/discovery/subscriber/zk_config.go) recordstag-route,condition-route, anddynamic-configadded/updated/deleted eventsvia
recordConfigPlatformEvent().ZooKeeper metadata change events
ZKMetadataEventSubscriber(pkg/core/discovery/subscriber/zk_metadata.go) recordsprovider and consumer metadata added/updated events via
recordMetadataPlatformEvent().Nacos instance registration/deregistration events
NacosServiceEventSubscriber(pkg/core/discovery/subscriber/nacos_service.go)records
Nacos instance registered,deregistered, andupdatedevents withapplication name, IP, and port via
recordNacosInstanceEvents().Nacos consumer metadata change events
Same subscriber records
Nacos consumer metadata added/updatedeventsvia
recordNacosConsumerEvents().PlatformEvent store indexes (
pkg/core/store/index/platform_event.go)ByPlatformEventAppName,ByPlatformEventInstanceName,ByPlatformEventInstanceIP,ByPlatformEventServiceName,ByPlatformEventSourceType— enables per-entity event queries.Unified Event Timeline Query Service
pkg/console/service/event.go)GET /application/eventGET /instance/eventGET /service/eventEach endpoint queries both
K8sEventandPlatformEventstores in parallel,merges results, sorts by time descending, and returns paginated unified timeline entries.
{ "code": "Success", "message": "success", "data": { "list": [ { "time": "2026-06-07 17:39:14", "type": "warning", "message": "Error updating Endpoint Slices...", "source": "endpoint-slice-controller" }, { "time": "2026-06-07 10:25:04", "type": "normal", "message": "Zookeeper provider metadata added: demo-app → com.example.TestService", "source": "Zookeeper" }, { "time": "2026-06-07 10:25:05", "type": "normal", "message": "Nacos instance registered: shop-order 10.244.1.29:20882", "source": "Nacos" } ], "total": 78 } }Frontend Event Timeline Visualization
EventTimeline shared component (
ui-vue3/src/components/EventTimeline.vue)Reusable Vue component with alternating left/right timeline layout using Ant Design.
Each event node displays: timestamp, icon (check-circle for normal, warning for warnings),
message text, and source tag. Supports infinite scroll with "load more" pagination.
Degraded/expired events show an empty state with "过期事件不会存储" (expired events
are not stored) watermark.
Application event tab (
ui-vue3/src/views/resources/applications/tabs/event.vue)Wires the EventTimeline component into the application detail page as an "事件" tab.
Queries events filtered by
appNameand currentmesh.Instance event tab (
ui-vue3/src/views/resources/instances/tabs/event.vue)Wires the EventTimeline component into the instance detail page as an "事件" tab.
Queries events filtered by
instanceNameandinstanceIP.Service event tab (
ui-vue3/src/views/resources/services/tabs/event.vue)Wires the EventTimeline component into the service detail page as an "事件" tab.
Queries events filtered by
serviceName.Event tab restored in routes (
ui-vue3/src/router/defaultRoutes.ts)Event tab routes previously commented out are now enabled for all three resource types.
Minor Improvements
pkg/core/events/component.go)"no subscriber for resource" message downgraded from
INFOtoDEBUGto reducelog noise (991 occurrences per 5 minutes for
K8sEventandService).📁 Affected Areas / Review Checklist
api/mesh/v1alpha1/k8s_event.go,k8s_event.protopkg/console/service/event.go,handler/event.go,model/event.go,router/router.gopkg/core/events/component.gopkg/core/resource/apis/mesh/v1alpha1/k8sevent_types.go,platformevent_types.gopkg/core/store/index/k8s_event.go,platform_event.gopkg/core/discovery/subscriber/nacos_service.go,zk_config.go,zk_metadata.gopkg/core/discovery/subscriber/platform_event_recorder.gopkg/engine/kubernetes/factory.go,listerwatcher/k8s_event.goui-vue3/src/components/EventTimeline.vueui-vue3/src/views/resources/{applications,instances,services}/tabs/event.vueui-vue3/src/api/service/{app,instance,service}.tsui-vue3/src/router/defaultRoutes.tsui-vue3/src/types/api.ts🔄 Data Flow Architecture
🧪 E2E Verification
Tested against a 6-node kind cluster (
cluster-ha-new, k8s v1.35.1) with:🔮 Known Limitations
K8s events use mesh "default" — K8s events are not associated with any discovery
mesh. The frontend registry selector must be set to "default" (or unset) to view
K8s events. There is a UX gap: the registry dropdown does not show "default" as an
explicit option.
No event TTL / cleanup — Events accumulate indefinitely in the store. There is
no retention policy, TTL, or automatic cleanup mechanism for any store backend.
No subscriber for K8sEvent and Service — K8sEvent and Service resources are
stored but no event subscriber processes them (by design — they only need storage).
The EventBus emits a "no subscriber" debug log for these kinds.
PlatformEvent bypasses EventBus — PlatformEvents are created as side-effects
within subscriber
ProcessEventmethods and stored directly viaeventStore.Add(), bypassing the EventBus entirely. This means PlatformEventsthemselves cannot be observed by downstream subscribers.