Documentation
¶
Overview ¶
Package monitoring exposes container metrics and workspace health overviews.
Index ¶
- Variables
- type AppHealth
- type NodeDocker
- type Overview
- type RecentEvent
- type ServerInfo
- type Service
- func (s *Service) AppMetrics(ctx context.Context, workspaceID, appID uint) (docker.StatsSample, error)
- func (s *Service) History(workspaceID, appID uint, since time.Time, limit int) ([]models.MetricSample, error)
- func (s *Service) SetServerInfo(si ServerInfo)
- func (s *Service) StartScraper(ctx context.Context, interval, retention time.Duration)
- func (s *Service) StreamAppLogs(ctx context.Context, workspaceID, appID uint, follow bool, tail string, ...) error
- func (s *Service) StreamAppMetrics(ctx context.Context, workspaceID, appID uint, ...) error
- func (s *Service) StreamWorkspaceUsage(ctx context.Context, workspaceID uint, interval time.Duration, ...) error
- func (s *Service) WorkspaceLiveUsage(ctx context.Context, workspaceID uint) (WorkspaceSample, error)
- func (s *Service) WorkspaceOverview(workspaceID uint) (Overview, error)
- func (s *Service) WorkspaceUsageHistory(workspaceID uint, since time.Time, bucket time.Duration) ([]WorkspaceHistoryPoint, error)
- type WorkspaceHistoryPoint
- type WorkspaceSample
Constants ¶
This section is empty.
Variables ¶
var ErrNoActiveContainer = errors.New("application has no active container")
ErrNoActiveContainer is returned when an app has no running release.
var ErrTaskOnUnmanagedNode = errors.New(
"this app's task runs on a swarm node with no Miabi agent, so its resource usage cannot be read. " +
"Add the node to Miabi (install the agent) to see metrics, stats and a shell")
ErrTaskOnUnmanagedNode is the user-facing form of nodes.ErrTaskUnreachable: the app IS running, but on a swarm node with no Miabi agent, so there is no engine to read its container's resource usage through. Docker offers no manager-side equivalent of stats, so this is a hard limit, not a bug. Logs are unaffected — the manager aggregates those (see StreamAppLogs).
Functions ¶
This section is empty.
Types ¶
type AppHealth ¶
type AppHealth struct {
ID uint `json:"id"`
Name string `json:"name"` // unique slug handle
DisplayName string `json:"display_name"` // free-text label
Status models.AppStatus `json:"status"`
Health string `json:"health"` // healthy | unhealthy | unknown
ServerID uint `json:"server_id"`
ServerName string `json:"server_name,omitempty"` // node the app runs on
CreatedAt time.Time `json:"created_at"`
}
AppHealth describes one application's health.
type NodeDocker ¶
type NodeDocker interface {
For(serverID uint) (docker.Client, error)
// ForServiceTask finds the engine holding a swarm service's task container.
// A service has no fixed node, and only the node running the task can see it.
ForServiceTask(ctx context.Context, serviceName string) (docker.Client, string, error)
}
NodeDocker resolves the Docker client for a node id (0 = local). Lets metrics and log/stat streams reach an app's container on whichever node it runs.
type Overview ¶
type Overview struct {
Apps []AppHealth `json:"apps"`
TotalApps int `json:"total_apps"`
Running int `json:"running"`
Failed int `json:"failed"`
Databases int `json:"databases"`
Stacks int `json:"stacks"`
RecentEvents []RecentEvent `json:"recent_events"`
}
Overview is a workspace-level summary.
type RecentEvent ¶
type RecentEvent struct {
models.AppEvent
AppName string `json:"app_name"` // unique slug handle
AppDisplayName string `json:"app_display_name"` // free-text label
}
RecentEvent is a workspace activity-feed entry: an application event enriched with the originating application's handle/label so the dashboard can show which app it belongs to without an extra lookup.
type ServerInfo ¶
ServerInfo resolves a node record by id, so the overview can label each app with the node it runs on.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
func NewService ¶
func NewService(apps *repositories.ApplicationRepository, releases *repositories.ReleaseRepository, dbs *repositories.DatabaseRepository, stacks *repositories.StackRepository, events *repositories.AppEventRepository, metrics *repositories.MetricRepository, clients NodeDocker) *Service
func (*Service) AppMetrics ¶
func (s *Service) AppMetrics(ctx context.Context, workspaceID, appID uint) (docker.StatsSample, error)
AppMetrics returns a single resource-usage sample for an app's active container.
func (*Service) History ¶
func (s *Service) History(workspaceID, appID uint, since time.Time, limit int) ([]models.MetricSample, error)
History returns stored metric samples for an app since `since`. The app is verified to belong to workspaceID first so a caller cannot read another workspace's stored metrics by guessing its app id; an app that isn't in the workspace yields an empty history rather than confirming it exists elsewhere.
func (*Service) SetServerInfo ¶
func (s *Service) SetServerInfo(si ServerInfo)
SetServerInfo wires the resolver used to label apps with their node's name.
func (*Service) StartScraper ¶
StartScraper runs a background loop that samples every running app's container at `interval` and prunes samples older than `retention`. It returns when ctx is cancelled.
func (*Service) StreamAppLogs ¶
func (s *Service) StreamAppLogs(ctx context.Context, workspaceID, appID uint, follow bool, tail string, sink func(docker.LogLine) error) error
StreamAppLogs streams the active container's runtime logs (stdout/stderr), starting with the last `tail` lines. When follow is true it then follows live output until the context is cancelled; when false it returns after the tail.
A cluster (service) app is read from the MANAGER via `docker service logs`, not from a container. That is deliberate and is the only thing that works in general: Swarm may have placed the task on a node Miabi has no Docker client for (an unmanaged swarm member with no agent), and the manager can still pull its logs over the swarm control plane. It also aggregates every replica, which reading a single container never could.
func (*Service) StreamAppMetrics ¶
func (s *Service) StreamAppMetrics(ctx context.Context, workspaceID, appID uint, sink func(docker.StatsSample) error) error
StreamAppMetrics streams live samples for an app's active container.
func (*Service) StreamWorkspaceUsage ¶
func (s *Service) StreamWorkspaceUsage(ctx context.Context, workspaceID uint, interval time.Duration, sink func(WorkspaceSample) error) error
StreamWorkspaceUsage pushes an aggregate sample immediately, then every interval until ctx is cancelled (the SSE client disconnects). interval is floored so a client cannot drive excessive sampling.
func (*Service) WorkspaceLiveUsage ¶
func (s *Service) WorkspaceLiveUsage(ctx context.Context, workspaceID uint) (WorkspaceSample, error)
WorkspaceLiveUsage samples every running container in the workspace once (concurrently) and returns the aggregate. Containers that error mid-sample — stopped just now, or on an unreachable node — are skipped, so one bad container never fails the whole reading.
func (*Service) WorkspaceOverview ¶
WorkspaceOverview aggregates app health and resource counts for a workspace.
func (*Service) WorkspaceUsageHistory ¶
func (s *Service) WorkspaceUsageHistory(workspaceID uint, since time.Time, bucket time.Duration) ([]WorkspaceHistoryPoint, error)
WorkspaceUsageHistory builds a workspace-level resource time series from the scraper's stored per-app samples: each sample is bucketed to `bucket` and summed across the workspace's apps, so the series reflects total workspace consumption over time. Powers the dashboard sparkline. Within a bucket an app is counted once (its latest sample), so a bucket wider than the scrape interval never double-counts.
type WorkspaceHistoryPoint ¶
type WorkspaceHistoryPoint struct {
At time.Time `json:"at"`
CPUPercent float64 `json:"cpu_percent"`
CPUCores float64 `json:"cpu_cores"`
MemoryBytes uint64 `json:"memory_bytes"`
}
WorkspaceHistoryPoint is one time-bucketed aggregate of the workspace's stored per-app metric samples (from the scraper), summed across its applications.
type WorkspaceSample ¶
type WorkspaceSample struct {
At time.Time `json:"at"`
Containers int `json:"containers"` // running containers sampled
CPUPercent float64 `json:"cpu_percent"` // summed; 100% == one core fully used
CPUCores float64 `json:"cpu_cores"` // cpu_percent / 100 (convenience)
MemoryBytes uint64 `json:"memory_bytes"` // actual resident memory in use
MemoryLimitBytes uint64 `json:"memory_limit_bytes"` // summed per-container limits (0 = uncapped)
NetRxBytes uint64 `json:"net_rx_bytes"`
NetTxBytes uint64 `json:"net_tx_bytes"`
}
WorkspaceSample is a live, aggregated resource snapshot across a workspace's running application and database containers. It reflects what the containers actually consume right now — unlike the /usage endpoint, which reports declared reservations and quota counts.