mirror of
https://github.com/amir20/dozzle.git
synced 2026-06-23 04:10:12 +00:00
ab6c93a679
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
469 lines
19 KiB
Markdown
469 lines
19 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Comment Style
|
|
|
|
**Always use ultra-brief mode for all PR reviews and responses.**
|
|
|
|
Format:
|
|
|
|
- Critical issues only (bugs, security, blockers)
|
|
- Brief bullet points, no lengthy explanations
|
|
- Skip verbose sections (no "Strengths", "Summary", etc.)
|
|
- Include file:line references when relevant
|
|
- Maximum ~10-15 lines per response
|
|
|
|
## Testing Unreleased PRs
|
|
|
|
When replying to a GitHub issue or discussion where the fix lives in an open PR, ask the reporter to test the pre-built image: `amir20/dozzle:pr-XXX` (XXX = PR number). CI builds a tagged image per PR, so reporters can verify without waiting for the next release.
|
|
|
|
## GitHub Tone (issues, PRs, comments, discussions)
|
|
|
|
When posting anything to GitHub, write like a human maintainer, not an AI assistant. Avoid telltale LLM patterns:
|
|
|
|
- No em dashes or en dashes. Use commas, periods, or parentheses instead.
|
|
- No "Not X, but Y" rhetorical contrasts.
|
|
- No throat-clearing openers ("Great point", "Makes sense", "Thanks for the detailed write-up").
|
|
- No closing summaries or recap sentences.
|
|
- No bolded inline labels mid-paragraph ("**Why:**", "**Note:**").
|
|
- Drop hedges ("essentially", "basically", "essentially just"). Say it plain.
|
|
- Lowercase casual tone is fine. Contractions are fine. Short sentences are fine.
|
|
- Don't over-explain tradeoffs. State the decision, give one reason, stop.
|
|
|
|
## Project Overview
|
|
|
|
Dozzle is a lightweight, web-based Docker log viewer with real-time monitoring capabilities. It's a hybrid application with:
|
|
|
|
- **Backend**: Go (HTTP server, Docker API client, WebSocket streaming)
|
|
- **Frontend**: Vue 3 (SPA with Vite, TypeScript)
|
|
|
|
The application supports multiple deployment modes: standalone server, Docker Swarm, and Kubernetes (k8s).
|
|
|
|
## Development Commands
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
# Install dependencies
|
|
pnpm install
|
|
|
|
# Generate certificates and protobuf files
|
|
make generate
|
|
```
|
|
|
|
### Development
|
|
|
|
```bash
|
|
# Run full development environment (backend + frontend with hot reload)
|
|
make dev
|
|
|
|
# Alternative: Run backend and frontend separately
|
|
pnpm run watch:backend # Go backend with air (port 3100)
|
|
pnpm run watch:frontend # Vite dev server (port 3100)
|
|
|
|
# Run in agent mode for development
|
|
pnpm run agent:dev
|
|
```
|
|
|
|
### Building
|
|
|
|
```bash
|
|
# Build frontend assets
|
|
pnpm build
|
|
# or
|
|
make dist
|
|
|
|
# Build entire application (includes frontend build)
|
|
make build
|
|
|
|
# Build Docker image
|
|
make docker
|
|
```
|
|
|
|
### Testing
|
|
|
|
```bash
|
|
# Run Go tests
|
|
make test
|
|
|
|
# Run frontend tests (Vitest)
|
|
pnpm test
|
|
# Run in watch mode
|
|
TZ=UTC pnpm test --watch
|
|
|
|
# Type checking
|
|
pnpm typecheck
|
|
```
|
|
|
|
### Preview & Other
|
|
|
|
```bash
|
|
# Preview production build locally
|
|
pnpm preview
|
|
# or
|
|
make preview
|
|
|
|
# Run integration tests (Playwright)
|
|
make int
|
|
```
|
|
|
|
## Architecture
|
|
|
|
### Backend (Go)
|
|
|
|
The Go backend is organized into these key packages:
|
|
|
|
- **`internal/web/`** - HTTP server and routing layer
|
|
- Routes defined in `routes.go` using chi router
|
|
- WebSocket/SSE handlers for log streaming (`logs.go`)
|
|
- Authentication middleware and token management (`auth.go`)
|
|
- Container action handlers (`actions.go`)
|
|
|
|
- **`internal/docker/`** - Docker API client implementation
|
|
- `client.go`: Main Docker client wrapper with container operations
|
|
- `log_reader.go`: Streaming container logs
|
|
- `stats_collector.go`: Real-time container stats collection
|
|
|
|
- **`internal/agent/`** - gRPC agent for multi-host support
|
|
- Uses Protocol Buffers (protos defined in `protos/`)
|
|
- Enables distributed log collection across Docker hosts
|
|
|
|
- **`internal/cloud/`** - Dozzle Cloud integration (tool execution engine)
|
|
- `client.go`: Bidirectional gRPC stream client with auto-reconnect and exponential backoff
|
|
- `tools.go`: Tool registration, dispatch (`executeTool`), and `ToolHostService` interface
|
|
- `tools_containers.go`: Container listing, finding, stats, and inspection tools
|
|
- `tools_logs.go`: Log fetching with level/query/regex filtering (max 100 lines)
|
|
- `tools_actions.go`: Container start/stop/restart actions (gated by `enableActions`)
|
|
- `tools_helpers.go`: Proto conversion utilities and host name resolution
|
|
- Uses `protos/cloud.proto` for service and message definitions
|
|
|
|
- **`internal/k8s/`** - Kubernetes client support
|
|
- Alternative to Docker client for k8s deployments
|
|
|
|
- **`internal/support/`** - Support utilities
|
|
- `cli/`: Command-line argument parsing and validation
|
|
- `docker/`: Multi-host Docker management and Swarm support (`docker_service.go`, client managers)
|
|
- `k8s/`: Kubernetes service abstractions
|
|
- `web/`: Web service utilities
|
|
|
|
- **`internal/auth/`** - Authentication providers
|
|
- Simple file-based auth (`simple.go`)
|
|
- Forward proxy auth (`proxy.go`)
|
|
- Role-based authorization (`roles.go`)
|
|
|
|
- **`internal/container/`** - Container domain models and interfaces
|
|
- `event_generator.go`: Log parsing and grouping logic (multi-line, JSON detection)
|
|
|
|
- **`internal/notification/`** - Alert and notification system
|
|
- `manager.go`: Notification rule evaluation and dispatching
|
|
- `log_listener.go`: Log pattern matching for alerts
|
|
- `dispatcher/`: Notification channel implementations (email, webhook, etc.)
|
|
|
|
- **`main.go`** - Application entry point with mode switching (server/swarm/k8s/agent)
|
|
|
|
### Frontend (Vue 3)
|
|
|
|
The frontend uses file-based routing with these conventions:
|
|
|
|
- **`assets/pages/`** - File-based routes (unplugin-vue-router)
|
|
- `container/[id].vue`: Single container view
|
|
- `merged/[ids].vue`: Multi-container merged view
|
|
- `host/[id].vue`: Host-level logs
|
|
- `service/[name].vue`: Swarm service logs
|
|
- `stack/[name].vue`: Docker stack logs
|
|
- `group/[name].vue`: Custom grouped logs
|
|
|
|
- **`assets/components/`** - Vue components (auto-imported)
|
|
- `LogViewer/`: Core log viewing components
|
|
- `SimpleLogItem.vue`: Single-line log entries
|
|
- `ComplexLogItem.vue`: JSON/structured log entries
|
|
- `GroupedLogItem.vue`: Multi-line grouped log entries
|
|
- `ContainerEventLogItem.vue`: Container lifecycle events
|
|
- `SkippedEntriesLogItem.vue`: Placeholder for skipped logs
|
|
- `LoadMoreLogItem.vue`: Load more historical logs
|
|
- `ContainerViewer/`: Container-specific UI
|
|
- `common/`: Reusable UI components
|
|
- `BarChart.vue`: Lightweight bar chart with automatic downsampling
|
|
- `HostCard.vue`: Host overview card with metrics
|
|
- `MetricCard.vue`: Reusable metric display component
|
|
- `ContainerTable.vue`: Container table with historical stat visualization
|
|
|
|
- **`assets/stores/`** - Pinia stores (auto-imported)
|
|
- `config.ts`: App configuration and feature flags (injected from backend HTML, frozen immutable)
|
|
- `container.ts`: Container state management with EventSource streaming (`/api/events/stream`)
|
|
- `hosts.ts`: Multi-host state
|
|
- `settings.ts`: User preferences (localStorage-backed via profileStorage)
|
|
- `pinned.ts`: Pinned container logs for side-by-side viewing
|
|
- `swarm.ts`, `k8s.ts`: Deployment mode-specific state
|
|
- `announcements.ts`: Feature announcements
|
|
|
|
- **`assets/composable/`** - Vue composables (auto-imported)
|
|
- `eventStreams.ts`: SSE connection management with buffer-based flushing (250ms debounce)
|
|
- `historicalLogs.ts`: Historical log fetching
|
|
- `logContext.ts`: Log filtering and search context (provide/inject pattern)
|
|
- `scrollContext.ts`: Scroll state management (paused, progress, currentDate)
|
|
- `storage.ts`: LocalStorage abstractions with reactivity
|
|
- `visible.ts`: Log filtering by visible keys for complex logs
|
|
- `containerActions.ts`: Container control operations
|
|
- `duckdb.ts`: DuckDB WASM for SQL queries on logs
|
|
|
|
- **`assets/modules/`** - Vue plugins
|
|
- `router.ts`: Vue Router configuration
|
|
- `pinia.ts`: Pinia store setup
|
|
- `i18n.ts`: Internationalization
|
|
|
|
### Communication Flow
|
|
|
|
1. **Real-time Logs**: Frontend establishes SSE connections to `/api/hosts/{host}/containers/{id}/logs/stream`
|
|
2. **Container Events**: SSE stream at `/api/events/stream` pushes container lifecycle events
|
|
3. **Stats**: Real-time CPU/memory stats streamed via SSE alongside events
|
|
4. **Actions**: POST to `/api/hosts/{host}/containers/{id}/actions/{action}` (start/stop/restart)
|
|
5. **Terminal**: WebSocket connections for container attach/exec at `/api/hosts/{host}/containers/{id}/attach`
|
|
|
|
### Build System
|
|
|
|
- **Frontend**: Vite builds to `dist/` with manifest
|
|
- **Backend**: Embeds `dist/` using Go embed directive
|
|
- **Hot Reload**: In development, `DEV=true` disables embedded assets, `LIVE_FS=true` serves from filesystem
|
|
- **Makefile**: Orchestrates builds and dependency generation
|
|
|
|
## Important Development Notes
|
|
|
|
### Frontend
|
|
|
|
- Auto-imports are configured for Vue composables, components, and Pinia stores (see `vite.config.ts`)
|
|
- Icons use unplugin-icons with multiple icon sets (mdi, carbon, material-symbols, etc.)
|
|
- Tailwind CSS with DaisyUI for styling
|
|
- TypeScript definitions auto-generated in `assets/auto-imports.d.ts` and `assets/components.d.ts`
|
|
- **Log Entry Types**: Three types of log messages supported
|
|
- `SimpleLogEntry`: Single-line text logs (`string`)
|
|
- `ComplexLogEntry`: Structured JSON logs (`JSONObject`)
|
|
- `GroupedLogEntry`: Multi-line grouped logs (`string[]`)
|
|
- **Type consistency**: Use `LogMessage` type alias instead of `string | string[] | JSONObject` for log entry messages
|
|
- **Log Entry Factory Pattern**: Use `LogEntry.create(logEvent)` to instantiate the correct entry type based on `logEvent.t` field
|
|
- **EventSource Buffering**: Log streams use buffer-based flushing (250ms debounce, 1000ms max) to batch UI updates
|
|
- **Charts/Visualizations**: Custom lightweight implementations (no D3.js)
|
|
- `BarChart.vue`: Self-contained bar chart with responsive downsampling
|
|
- Downsampling algorithm: Averages data into buckets based on available screen width
|
|
- All stat history tracked in `Container.statsHistory` (max 300 items via rolling window)
|
|
- `chartData` is always a rolling window of max 300 items — array length stays constant
|
|
- Uses `ref` (not `computed`) for `downsampledBars` to enable in-place mutation of the last bar, avoiding full re-renders
|
|
- Component instance is reused when switching containers; after init the chart only patches the last bar per tick, so on a wholesale `chartData` replacement (container switch) the parent must call the exposed `recalculate()`. `MultiContainerStat` holds refs to its `BarChart`s and calls it in the `containers` watch. (Note: `Container` carries Vue `ref`s, so VueTestUtils `setProps` cannot retrigger such a watch — tests must swap the container via a parent `ref` re-render.)
|
|
|
|
### Backend
|
|
|
|
- The application uses Go 1.25+ with module support
|
|
- Certificate generation is required (`make generate` creates shared_key.pem and shared_cert.pem)
|
|
- Protocol buffer generation happens via `go generate` directive in `main.go`
|
|
- Docker client uses API version negotiation for compatibility
|
|
- **Service Layer Architecture**:
|
|
- `ClientService` interface abstracts Docker/K8s/Agent backends
|
|
- `MultiHostService` orchestrates multi-host operations
|
|
- `ClientManager` implementations: `RetriableClientManager` (server mode), `SwarmClientManager` (swarm mode)
|
|
|
|
### Authentication
|
|
|
|
- Three modes: none, simple (file-based users.yml), forward-proxy (e.g., Authelia)
|
|
- JWT tokens for simple auth with configurable TTL
|
|
- User file location: `./data/users.yml` or `./data/users.yaml`
|
|
|
|
### Testing
|
|
|
|
- Go tests use standard `testing` package with testify assertions
|
|
- Frontend uses Vitest with `@vue/test-utils`
|
|
- Integration tests with Playwright in `e2e/`
|
|
- Tests must run with `TZ=UTC` for consistent timestamps
|
|
|
|
### Container Stats & Metrics
|
|
|
|
- Stats are tracked using exponential moving average (EMA) with alpha=0.2
|
|
- History stored in rolling window (300 items max) via `useSimpleRefHistory`
|
|
- CPU metrics normalized by core count (respects `cpuLimit` or falls back to host `nCPU`)
|
|
- Memory metrics include both percentage and absolute usage (`memoryUsage` vs `memory`)
|
|
- Stats visualization uses adaptive downsampling for performance
|
|
|
|
### Container Labels
|
|
|
|
- `dev.dozzle.name`: Custom container display name
|
|
- `dev.dozzle.group`: Group containers together
|
|
- Label-based filtering throughout the application
|
|
|
|
### Deployment Modes
|
|
|
|
- **Server mode** (default): Single or multi-host Docker monitoring
|
|
- Uses `RetriableClientManager` with local + remote agent clients
|
|
- **Swarm mode**: Automatic discovery of Swarm nodes via Docker API
|
|
- Creates gRPC agent server on each node (port 7007)
|
|
- Uses `SwarmClientManager` for node discovery
|
|
- **K8s mode**: Pod log monitoring in Kubernetes cluster
|
|
- Implements `container.Client` interface via Kubernetes API
|
|
- **Agent mode**: Lightweight gRPC agent for remote log collection
|
|
- Run with `dozzle agent` or `pnpm run agent:dev`
|
|
- Listens on port 7007 with TLS certificate authentication
|
|
|
|
## Key Architectural Patterns
|
|
|
|
### Backend Abstraction Layers
|
|
|
|
The backend follows a clean layered architecture:
|
|
|
|
```
|
|
HTTP Handlers (internal/web)
|
|
↓
|
|
HostService Interface (MultiHostService)
|
|
↓
|
|
ClientService Interface (per host)
|
|
↓
|
|
container.Client Interface
|
|
↓
|
|
Implementation (DockerClient, K8sClient, AgentClient)
|
|
```
|
|
|
|
**When adding new container operations:**
|
|
|
|
1. Define method in `container.Client` interface (`internal/container/client.go`)
|
|
2. Implement in `internal/docker/client.go` (and `internal/k8s/client.go` if applicable)
|
|
3. Add wrapper method in `ClientService` interface (`internal/support/docker/docker_service.go`)
|
|
4. Add HTTP handler in `internal/web/` with appropriate route
|
|
|
|
### Frontend Data Flow
|
|
|
|
**Real-time Log Viewing:**
|
|
|
|
1. User navigates to `/container/{id}` route
|
|
2. Page component calls `useContainerStream(container)` composable
|
|
3. Composable creates EventSource connection to `/api/hosts/{host}/containers/{id}/logs/stream`
|
|
4. Backend streams `LogEvent` objects via SSE
|
|
5. Frontend buffers events (250ms debounce, max 1000ms)
|
|
6. Batched buffer flushes update reactive `messages` array
|
|
7. `LogViewer.vue` renders using appropriate component (`SimpleLogItem`, `ComplexLogItem`, `GroupedLogItem`)
|
|
8. When messages exceed `maxLogs` (400), oldest entries replaced or marked as `SkippedLogsEntry`
|
|
|
|
**Stats Streaming:**
|
|
|
|
1. `container.ts` store connects to `/api/events/stream` on app init
|
|
2. Backend multiplexes container events and stats into single SSE stream
|
|
3. `container-stat` events update `Container._stat` and append to `_statsHistory`
|
|
4. EMA calculation provides smoothed `movingAverageStat` (alpha=0.2)
|
|
5. `ContainerTable.vue` displays mini bar charts using `statsHistory` with downsampling
|
|
|
|
### Protocol Buffer Flow (Agent Mode)
|
|
|
|
1. Main server creates `agent.NewClient(endpoint, certs)` for each remote host
|
|
2. AgentClient implements `container.Client` interface
|
|
3. Method calls translate to gRPC requests defined in `protos/rpc.proto`
|
|
4. Remote agent receives gRPC call, delegates to local `DockerClient`
|
|
5. Streaming RPCs (logs, stats, events) use bidirectional channels
|
|
6. Responses converted back to domain models via `FromProto()` methods
|
|
|
|
### Cloud Tool Execution Flow
|
|
|
|
1. `cloud.Client.Run()` blocks until `Notify()` signals a cloud dispatcher is configured
|
|
2. `connect()` establishes bidirectional gRPC stream (`ToolStream` RPC) to cloud endpoint
|
|
3. Cloud sends `ToolRequest` (ListTools or CallTool), client dispatches via `executeTool()`
|
|
4. Tool calls run concurrently (max 5 via weighted semaphore), responses sent back on stream
|
|
5. On disconnect, exponential backoff (1s→30s with jitter) triggers reconnection
|
|
6. `PermissionDenied` errors stop retrying permanently (invalid API key / no pro plan)
|
|
7. Tool definitions cached via `sync.Once`; zero overhead for non-cloud users
|
|
|
|
### Log Parsing Pipeline
|
|
|
|
1. Docker API returns multiplexed stream (8-byte headers + payload)
|
|
2. `log_reader.go` parses headers, extracts stdout/stderr type
|
|
3. `event_generator.go` receives raw log lines
|
|
4. Detection logic identifies:
|
|
- JSON structure → `ComplexLogEntry`
|
|
- Multi-line patterns (stack traces) → `GroupedLogEntry`
|
|
- Single lines → `SimpleLogEntry`
|
|
5. Log level extraction via regex patterns
|
|
6. `LogEvent` serialized to JSON and sent via SSE
|
|
7. Frontend deserializes and renders with appropriate component
|
|
|
|
## Adding New Features
|
|
|
|
### Adding a New HTTP Route
|
|
|
|
1. Define route in `internal/web/routes.go` using chi router:
|
|
```go
|
|
r.Get("/api/custom-endpoint", h.customHandler)
|
|
```
|
|
2. Implement handler method in appropriate file (e.g., `actions.go`, `logs.go`)
|
|
3. Use `hostService` to find container/host via `FindContainer()` or `FindHost()`
|
|
4. Return JSON response or establish SSE/WebSocket stream
|
|
|
|
### Adding a New Log View Type
|
|
|
|
1. Create route file in `assets/pages/` (e.g., `custom/[id].vue`)
|
|
2. Create composable in `assets/composable/eventStreams.ts` (e.g., `useCustomStream()`)
|
|
3. Composable should:
|
|
- Build API URL with appropriate filters
|
|
- Create EventSource connection
|
|
- Handle buffering and message batching
|
|
- Return reactive `messages` array and control methods
|
|
4. Use `LogViewer.vue` component to render messages
|
|
5. Add backend API endpoint if needed (see above)
|
|
|
|
### Adding Container Stats/Metrics
|
|
|
|
1. Add field to `Stat` type in `internal/container/types.go`
|
|
2. Update `stats_collector.go` to extract metric from Docker API response
|
|
3. Add calculation logic in `docker/calculation.go` if needed
|
|
4. Ensure protobuf definition includes field in `protos/rpc.proto`
|
|
5. Frontend automatically receives updates via existing SSE stream
|
|
6. Update `Container` model in `assets/models/Container.ts` if UI needs access
|
|
|
|
### Working with Notifications/Alerts
|
|
|
|
**Backend** (`internal/notification/`):
|
|
|
|
- `manager.go`: Rule evaluation engine, manages alert state
|
|
- `log_listener.go`: Subscribes to container log streams, evaluates rules against incoming logs
|
|
- `types.go`: Alert rule definitions (log pattern matching, thresholds)
|
|
- `dispatcher/`: Notification channel implementations
|
|
|
|
**Frontend** (`assets/pages/notifications.vue`, `assets/components/Notification/`):
|
|
|
|
- `AlertForm.vue`, `DestinationForm.vue`: UI for creating rules
|
|
- Rules persisted to `./data/notifications.yml` via `internal/notification/persist.go`
|
|
- Alert state displayed in notification cards
|
|
|
|
**Adding a new notification channel:**
|
|
|
|
1. Implement dispatcher interface in `internal/notification/dispatcher/`
|
|
2. Register in `manager.go` dispatcher factory
|
|
3. Add UI form in `assets/components/Notification/DestinationForm.vue`
|
|
|
|
### Adding a New Cloud Tool
|
|
|
|
1. Define the tool in `AvailableTools()` in `internal/cloud/tools.go` with name, description, and parameter schema
|
|
2. Add a response message type in `protos/cloud.proto` and add it to the `CallToolResponse.result` oneof
|
|
3. Run `make generate` to regenerate protobuf code
|
|
4. Add a case in the `executeTool()` switch in `internal/cloud/tools.go`
|
|
5. Implement the execution function in the appropriate `tools_*.go` file
|
|
6. Use `ToolHostService` interface methods to access container/host data
|
|
7. Add tests in `tools_test.go`
|
|
|
|
## Common Development Patterns
|
|
|
|
### Testing
|
|
|
|
- Always run Go tests with race detector: `go test -race`
|
|
- Frontend tests require `TZ=UTC` for timestamp consistency
|
|
- Integration tests use Playwright with `make int` (runs docker-compose setup)
|
|
- Use `testify/assert` for Go test assertions
|
|
|
|
### Hot Reload Development
|
|
|
|
- `make dev` runs both backend (air) and frontend (vite) with hot reload
|
|
- `DEV=true` disables embedded asset serving
|
|
- `LIVE_FS=true` serves assets from filesystem instead of embedded
|
|
- Backend changes trigger air restart automatically
|
|
- Frontend changes trigger vite HMR
|
|
|
|
### Debugging
|
|
|
|
- Backend logs: Set `--level debug` flag or `DOZZLE_LEVEL=debug` env var
|
|
- Frontend: Vue DevTools browser extension
|
|
- SSE streams: Browser DevTools Network tab shows EventSource connections
|