Skip to content

Commit 76440a8

Browse files
committed
logs
1 parent 0c8a302 commit 76440a8

File tree

2 files changed

+49
-38
lines changed

2 files changed

+49
-38
lines changed

internal/metrics/collector.go

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -199,28 +199,29 @@ type ServiceInfo struct {
199199

200200
// ContainerMetrics contains Docker/Podman container metrics
201201
type ContainerMetrics struct {
202-
ContainerID string `json:"container_id"`
203-
ContainerName string `json:"container_name"`
204-
ImageName string `json:"image_name"`
205-
ImageTag string `json:"image_tag"`
206-
Runtime string `json:"runtime"`
207-
Status string `json:"status"`
208-
Health string `json:"health"`
209-
StartedAt int64 `json:"started_at"`
210-
ExitCode *int16 `json:"exit_code"`
211-
CPUPercent float64 `json:"cpu_percent"`
212-
CPUSystemPercent float64 `json:"cpu_system_percent"`
213-
MemoryUsage uint64 `json:"memory_usage"`
214-
MemoryLimit uint64 `json:"memory_limit"`
215-
MemoryPercent float64 `json:"memory_percent"`
216-
NetRxBytes uint64 `json:"net_rx_bytes"`
217-
NetTxBytes uint64 `json:"net_tx_bytes"`
218-
BlockReadBytes uint64 `json:"block_read_bytes"`
219-
BlockWriteBytes uint64 `json:"block_write_bytes"`
220-
PIDsCurrent uint32 `json:"pids_current"`
221-
PIDsLimit uint32 `json:"pids_limit"`
222-
Ports string `json:"ports"`
223-
Labels string `json:"labels"`
202+
ContainerID string `json:"container_id"`
203+
ContainerName string `json:"container_name"`
204+
ImageName string `json:"image_name"`
205+
ImageTag string `json:"image_tag"`
206+
Runtime string `json:"runtime"`
207+
Status string `json:"status"`
208+
Health string `json:"health"`
209+
StartedAt int64 `json:"started_at"`
210+
ExitCode *int16 `json:"exit_code"`
211+
CPUPercent float64 `json:"cpu_percent"`
212+
CPUSystemPercent float64 `json:"cpu_system_percent"`
213+
MemoryUsage uint64 `json:"memory_usage"`
214+
MemoryLimit uint64 `json:"memory_limit"`
215+
MemoryPercent float64 `json:"memory_percent"`
216+
NetRxBytes uint64 `json:"net_rx_bytes"`
217+
NetTxBytes uint64 `json:"net_tx_bytes"`
218+
BlockReadBytes uint64 `json:"block_read_bytes"`
219+
BlockWriteBytes uint64 `json:"block_write_bytes"`
220+
PIDsCurrent uint32 `json:"pids_current"`
221+
PIDsLimit uint32 `json:"pids_limit"`
222+
Ports string `json:"ports"`
223+
Labels string `json:"labels"`
224+
RecentLogs []string `json:"recent_logs"` // Container logs (errors/warnings)
224225
}
225226

226227
// SystemSummary contains aggregated system metrics for the main dashboard
@@ -1189,10 +1190,10 @@ func registerContainerMetrics() error {
11891190
}
11901191

11911192
func registerLogMetrics() error {
1192-
// catops.log - Log entries from services (sent as individual metrics)
1193+
// catops.log - Log entries from containers (sent as individual metrics)
11931194
_, err := meter.Int64ObservableGauge(
11941195
"catops.log",
1195-
metric.WithDescription("Log entries from services"),
1196+
metric.WithDescription("Log entries from containers"),
11961197
metric.WithUnit("{entries}"),
11971198
metric.WithInt64Callback(func(ctx context.Context, o metric.Int64Observer) error {
11981199
cacheMu.RLock()
@@ -1202,22 +1203,22 @@ func registerLogMetrics() error {
12021203
return nil
12031204
}
12041205

1205-
// Send logs from services
1206-
for _, s := range m.Services {
1207-
if len(s.RecentLogs) == 0 {
1206+
// Send logs from containers (simple approach like self-hosted)
1207+
for _, c := range m.Containers {
1208+
if len(c.RecentLogs) == 0 {
12081209
continue
12091210
}
12101211

1211-
for idx, logLine := range s.RecentLogs {
1212+
for idx, logLine := range c.RecentLogs {
12121213
level := detectLogLevel(logLine)
12131214
attrs := []attribute.KeyValue{
1214-
attribute.String("source", s.LogSource),
1215-
attribute.String("source_path", s.ServiceName),
1215+
attribute.String("source", "docker"),
1216+
attribute.String("source_path", c.ContainerName),
12161217
attribute.String("level", level),
12171218
attribute.String("message", truncateString(logLine, 500)),
1218-
attribute.String("service", s.ServiceName),
1219-
attribute.String("container_id", s.ContainerID),
1220-
attribute.Int("pid", s.PID),
1219+
attribute.String("service", c.ContainerName),
1220+
attribute.String("container_id", c.ContainerID),
1221+
attribute.Int("pid", 0),
12211222
}
12221223
o.Observe(int64(idx), metric.WithAttributes(attrs...))
12231224
}
@@ -2021,8 +2022,12 @@ func collectDockerContainers() ([]ContainerMetrics, error) {
20212022
containers = append(containers, c)
20222023
}
20232024

2024-
// Skip docker inspect for each container - too expensive (N syscalls)
2025-
// Basic stats from "docker stats" are enough for monitoring
2025+
// Collect logs for containers using global log collector (for deduplication)
2026+
logCollector := GetLogCollector()
2027+
for i := range containers {
2028+
logs, _ := logCollector.CollectContainerLogs(containers[i].ContainerID)
2029+
containers[i].RecentLogs = logs
2030+
}
20262031

20272032
return containers, nil
20282033
}

internal/metrics/log_collector.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,12 +219,13 @@ func (lc *LogCollector) CollectServiceLogs(service *ServiceInfo) ([]string, stri
219219
return nil, ""
220220
}
221221

222-
// collectDockerLogs collects recent logs from a Docker container
223-
func (lc *LogCollector) collectDockerLogs(containerID string) ([]string, error) {
222+
// CollectContainerLogs collects logs directly from a container by ID
223+
// This is the simple approach like self-hosted - just get docker logs
224+
func (lc *LogCollector) CollectContainerLogs(containerID string) ([]string, error) {
224225
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(logTimeout)*time.Second)
225226
defer cancel()
226227

227-
// Get last N lines of logs
228+
// Get last N lines of logs with timestamps
228229
cmd := exec.CommandContext(ctx, "docker", "logs", "--tail", fmt.Sprintf("%d", maxLogLines), "--timestamps", containerID)
229230
output, err := cmd.CombinedOutput()
230231
if err != nil {
@@ -236,6 +237,11 @@ func (lc *LogCollector) collectDockerLogs(containerID string) ([]string, error)
236237
return lc.deduplicateLogs(filtered), nil
237238
}
238239

240+
// collectDockerLogs collects recent logs from a Docker container (legacy method for services)
241+
func (lc *LogCollector) collectDockerLogs(containerID string) ([]string, error) {
242+
return lc.CollectContainerLogs(containerID)
243+
}
244+
239245
// hashLogLine creates a hash of a log line for deduplication
240246
func (lc *LogCollector) hashLogLine(line string) string {
241247
hash := md5.Sum([]byte(line))

0 commit comments

Comments
 (0)