Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions apps/monitoring/containers/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,18 @@ func ShouldMonitorContainer(containerName string) bool {
return true
}

// GetServiceName returns the deduplication key of a container: its name
// without the swarm task suffix (myapp.1.abc123 → myapp), so replicas of the
// same swarm service are stored once per tick. Names without a task suffix
// (docker compose containers) are already unique per container and are kept
// as-is. Splitting on "-" here used to conflate distinct services sharing a
// name prefix (app-x-mysql and app-x-redis both mapped to "app-x"), so only
// the first of them in `docker stats` output was stored each tick — even
// though metrics are stored and queried by full container_name.
func GetServiceName(containerName string) string {
name := strings.TrimPrefix(containerName, "/")
parts := strings.Split(name, "-")
if len(parts) > 1 {
return strings.Join(parts[:len(parts)-1], "-")
if dot := strings.Index(name, "."); dot != -1 {
return name[:dot]
}
return name
}
27 changes: 27 additions & 0 deletions apps/monitoring/containers/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package containers

import "testing"

func TestGetServiceName(t *testing.T) {
cases := []struct {
name string
in string
want string
}{
{"swarm replica 1", "myapp-3fa2bc.1.zxc4vplq8m0e", "myapp-3fa2bc"},
{"swarm replica 2 collapses to the same service", "myapp-3fa2bc.2.a1b2c3d4e5f6", "myapp-3fa2bc"},
{"leading slash is stripped", "/myapp-3fa2bc.1.zxc4vplq8m0e", "myapp-3fa2bc"},
{"compose container_name stays distinct", "app-1425-mysql", "app-1425-mysql"},
{"sibling compose service must not collapse with the previous one", "app-1425-redis", "app-1425-redis"},
{"compose default naming stays distinct", "project-web-1", "project-web-1"},
{"name without separators", "single", "single"},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := GetServiceName(tc.in); got != tc.want {
t.Errorf("GetServiceName(%q) = %q, want %q", tc.in, got, tc.want)
}
})
}
}
Loading