kraken: use zenoh to show extension logs#3803
Open
nicoschmdt wants to merge 2 commits intobluerobotics:masterfrom
Open
kraken: use zenoh to show extension logs#3803nicoschmdt wants to merge 2 commits intobluerobotics:masterfrom
nicoschmdt wants to merge 2 commits intobluerobotics:masterfrom
Conversation
Reviewer's GuideRefactors extension log viewing to use a dedicated Vue modal component backed by Zenoh-based log streaming and historical log retrieval, replacing the old HTTP streaming container-logs endpoint and wiring backend Zenoh queryables for extension logs. Sequence diagram for extension logs retrieval via ZenohsequenceDiagram
actor User
participant ExtensionManagerView
participant ExtensionLogsModal
participant ZenohSession as Zenoh_ts_Session
participant BackendZenohAPI as Zenoh_router_backend
participant ZenohHandlers
participant ContainerManager
participant DockerEngine
participant ExtensionLogPublisher
User->>ExtensionManagerView: click ShowLogs(extension)
ExtensionManagerView->>ExtensionManagerView: initializeZenohSession()
ExtensionManagerView->>ZenohSession: Session.open(Config(url))
ZenohSession-->>ExtensionManagerView: zenoh_session
ExtensionManagerView->>ExtensionLogsModal: open with extensionIdentifier, extensionName, zenohSession
ExtensionLogsModal->>ExtensionLogsModal: openModal()
alt zenohSession not initialized
ExtensionLogsModal->>ExtensionLogsModal: modal_error = "Zenoh session not initialized"
else zenohSession ready
ExtensionLogsModal->>ExtensionLogsModal: requestHistoricalLogsForExtension(identifier)
ExtensionLogsModal->>ZenohSession: get("kraken/extension/logs/request?extension_name=identifier")
ZenohSession->>BackendZenohAPI: route query extension/logs/request
BackendZenohAPI->>ZenohHandlers: logs_request_handler(extension_name)
ZenohHandlers->>Extension: _fetch_settings()
ZenohHandlers->>ZenohHandlers: find matching ExtensionSettings
ZenohHandlers->>ExtensionLogPublisher: _topic_for(extension)
ZenohHandlers->>Extension: container_name()
ZenohHandlers->>ContainerManager: get_container_historical_logs(container_name)
ContainerManager->>DockerEngine: get_container_log_by_name(container_name)
DockerEngine-->>ContainerManager: raw_logs list
ContainerManager-->>ZenohHandlers: raw_logs
ZenohHandlers->>ExtensionLogPublisher: _extract_level(raw_line) for each line
ZenohHandlers-->>BackendZenohAPI: {status, messages, total_lines, topic}
BackendZenohAPI-->>ZenohSession: query reply
ZenohSession-->>ExtensionLogsModal: Receiver result Sample
ExtensionLogsModal->>ExtensionLogsModal: parse JSON, set modal_messages
ExtensionLogsModal->>ExtensionLogsModal: scrollToBottom()
ExtensionLogsModal->>ZenohSession: declare_subscriber(topic)
ZenohSession-->>ExtensionLogsModal: modal_subscriber
loop live logs
ExtensionLogPublisher->>ZenohSession: publish log Sample on topic
ZenohSession-->>ExtensionLogsModal: handler(sample)
ExtensionLogsModal->>ExtensionLogsModal: buffer message and flush to modal_messages
ExtensionLogsModal->>ExtensionLogsModal: scheduleScroll()
end
end
User->>ExtensionLogsModal: click Download
ExtensionLogsModal->>ExtensionLogsModal: downloadCurrentLog() create File
ExtensionLogsModal->>User: browser downloads safeName.log
User->>ExtensionLogsModal: close modal
ExtensionLogsModal->>ExtensionLogsModal: cleanup() undeclare subscriber
Class diagram for Zenoh-based extension logs componentsclassDiagram
class ExtensionManagerView {
<<VueComponent>>
+bool show_log
+string selected_log_extension_identifier
+string selected_log_extension_name
+Session zenoh_session
+mounted()
+destroyed()
+async initializeZenohSession()
+showLogs(extension)
}
class ExtensionLogsModal {
<<VueComponent>>
+bool value
+string extensionIdentifier
+string extensionName
+Session zenohSession
+LogMessage[] modal_messages
+Subscriber modal_subscriber
+string current_modal_topic
+string modal_error
+bool requesting_logs
+number query_timeout
+bool follow_logs
+bool scroll_pending
+LogMessage[] message_buffer
+number buffer_flush_timer
+async openModal()
+closeModal()
+cleanup()
+flushMessageBuffer()
+async setupModalSubscriber(topic)
+async requestHistoricalLogsForExtension(identifier)
+bool isMessageEmpty(msg)
+string extractLogMessage(msg)
+string formatLogMessage(msg)
+scheduleScroll()
+scrollToBottom(force)
+setErrorAndStop(message)
+string formatErrorType(errorType)
+downloadCurrentLog()
}
class LogMessage {
+string message
}
class ZenohHandlers {
+ZenohRouter router
+ZenohHandlers(router)
+async logs_request_handler(extension_name) dict
+register_queryables()
}
class ContainerManager {
+async get_container_log_by_name(container_name) AsyncGenerator~str~
+async get_container_historical_logs(container_name) List~str~
+async get_containers_stats() Dict
}
class ExtensionLogPublisher {
+static _topic_for(extension) string
+static _extract_level(raw_line) tuple
}
class ExtensionSettings {
+string identifier
+string name
+string container_name()
}
class Extension {
+static _fetch_settings() List~ExtensionSettings~
}
class ZenohRouter {
+add_routes_to_zenoh(application)
+add_queryable(key, handler)
}
class FastAPIZenohIntegration {
<<FastAPIApp>>
+ZenohRouter zenoh_router
+ZenohHandlers zenoh_handlers
+lifespan()
}
ExtensionManagerView --> ExtensionLogsModal : uses
ExtensionManagerView --> Session : holds zenoh_session
ExtensionLogsModal --> LogMessage : manages
ExtensionLogsModal --> Session : uses zenohSession
ExtensionLogsModal --> Subscriber : declares modal_subscriber
FastAPIZenohIntegration --> ZenohRouter : configures
FastAPIZenohIntegration --> ZenohHandlers : creates
ZenohHandlers --> ZenohRouter : registers queryables
ZenohHandlers --> Extension : reads settings
ZenohHandlers --> ExtensionLogPublisher : uses topic and level helpers
ZenohHandlers --> ContainerManager : requests Docker logs
ZenohHandlers --> ExtensionSettings : consumes instances
ContainerManager --> DockerEngine : interacts
class Session {
+static open(config) Session
+declare_subscriber(topic, options) Subscriber
+get(key, options) Receiver
+close()
}
class Subscriber {
+undeclare()
}
class Receiver {
+receive() Reply
}
class DockerEngine {
<<ExternalService>>
}
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- The historical logs path (
get_container_historical_logs→logs_request_handler) returns the full log list and sends all lines over Zenoh in one response; consider enforcing a maximum number of lines or a time/size window to prevent excessive memory usage and very large payloads for long-running containers. - In
ExtensionLogsModal.cleanup,undeclare()is called without awaiting while other code paths (setupModalSubscriber) await it; for consistency and to avoid dangling subscriptions, consider centralizing subscriber teardown in a single async helper that is always awaited from async contexts and called synchronously only when absolutely necessary.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The historical logs path (`get_container_historical_logs` → `logs_request_handler`) returns the full log list and sends all lines over Zenoh in one response; consider enforcing a maximum number of lines or a time/size window to prevent excessive memory usage and very large payloads for long-running containers.
- In `ExtensionLogsModal.cleanup`, `undeclare()` is called without awaiting while other code paths (`setupModalSubscriber`) await it; for consistency and to avoid dangling subscriptions, consider centralizing subscriber teardown in a single async helper that is always awaited from async contexts and called synchronously only when absolutely necessary.
## Individual Comments
### Comment 1
<location> `core/services/kraken/harbor/container.py:127-134` </location>
<code_context>
logger.info(f"Finished streaming logs for {container_name}")
+ @classmethod
+ async def get_container_historical_logs(cls, container_name: str) -> List[str]:
+ async with DockerCtx() as client:
+ try:
+ container = await cls.get_raw_container_by_name(client, container_name)
+ except ContainerNotFound as error:
+ raise StackedHTTPException(status_code=status.HTTP_404_NOT_FOUND, error=error) from error
+
+ return await container.log(stdout=True, stderr=True, follow=False, stream=False) # type: ignore
+
@classmethod
</code_context>
<issue_to_address>
**issue (bug_risk):** The type hint suggests a list of strings, but `container.log(..., stream=False)` may return bytes or a single string, which can break downstream processing.
`get_container_historical_logs` is annotated as `List[str]`, and `logs_request_handler` iterates over `raw_logs` assuming each element is a full line of text. However, `container.log(..., stream=False)` may return a single `str`/`bytes` blob or `bytes`, not a list of lines. That can cause iteration over bytes/characters and type issues.
To keep the contract accurate, normalize the result before returning:
- Decode `bytes` to `str` (e.g., UTF-8).
- If you get a single string, split it into lines (e.g., `splitlines()`) so the method always returns `List[str]`.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
core/frontend/src/components/kraken/modals/ExtensionLogsModal.vue
Outdated
Show resolved
Hide resolved
3388171 to
8b904aa
Compare
8b904aa to
5ba4952
Compare
5ba4952 to
6253510
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary by Sourcery
Switch extension log viewing to use zenoh-based streaming and historical retrieval with a dedicated logs modal.
New Features:
Enhancements:
Note
Medium Risk
Touches both UI and backend messaging paths for logs and introduces a new Zenoh queryable, so failures could break log visibility or add load when fetching large historical logs.
Overview
Switches extension log viewing from the
GET /container/{name}/logstreaming path to a Zenoh-based flow that queries historical logs and subscribes to live log topics.Frontend adds a dedicated
ExtensionLogsModal(follow + download, buffering/limits, error handling) and refactorsExtensionManagerViewto open it and manage a shared ZenohSession; the oldgetContainerLogsAPI call and client-side streaming/parsing code are removed.Backend registers a new Zenoh queryable (
extension/logs/request) that resolves an extension, returns recent container logs plus the topic to subscribe to, and addsContainerManager.get_container_historical_logsto support non-streaming log retrieval.Written by Cursor Bugbot for commit 6253510. This will update automatically on new commits. Configure here.