add better logs

This commit is contained in:
2026-04-04 14:37:53 +02:00
parent fe6ce5249e
commit 960e12f967
2 changed files with 55 additions and 20 deletions

View File

@@ -4,24 +4,43 @@ import (
"fmt"
"os/exec"
"runtime"
"strings"
"github.com/spf13/cobra"
)
var logsCmd = &cobra.Command{
Use: "logs",
Short: "Open the Dozzle log viewer in the browser",
Use: "logs [container]",
Short: "Open Dozzle in the browser, optionally deep-linking to a container",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
cfg, _, _, err := setup()
cfg, client, _, err := setup()
if err != nil {
return err
}
// Derive logs URL from Portainer URL (same base domain)
// e.g. https://portainer.home.jens.pub → https://logs.home.jens.pub
logsURL := deriveLogsURL(cfg.Portainer.URL)
fmt.Printf("Opening %s\n", logsURL)
return openBrowser(logsURL)
logsBase := deriveLogsURL(cfg.Portainer.URL)
if len(args) == 0 {
fmt.Printf("Opening %s\n", logsBase)
return openBrowser(logsBase)
}
containerName := args[0]
id, err := client.FindContainer(containerName)
if err != nil {
return err
}
// Dozzle uses a short ID (first 12 chars)
shortID := id
if len(shortID) > 12 {
shortID = shortID[:12]
}
url := logsBase + "/container/" + shortID
fmt.Printf("Opening %s\n", url)
return openBrowser(url)
},
}
@@ -30,24 +49,17 @@ func init() {
}
func deriveLogsURL(portainerURL string) string {
domain := stripScheme(portainerURL)
// Replace "portainer." prefix with "logs."
return fmt.Sprintf("https://logs.%s", domainFromURL(portainerURL))
if idx := strings.Index(domain, "."); idx != -1 {
return "https://logs." + domain[idx+1:]
}
func domainFromURL(u string) string {
// Strip scheme and find domain after first dot
u = stripScheme(u)
for i, c := range u {
if c == '.' {
return u[i+1:]
}
}
return u
return "https://logs." + domain
}
func stripScheme(u string) string {
for _, prefix := range []string{"https://", "http://"} {
if len(u) > len(prefix) && u[:len(prefix)] == prefix {
if strings.HasPrefix(u, prefix) {
return u[len(prefix):]
}
}

View File

@@ -38,6 +38,29 @@ type StackFile struct {
StackFileContent string `json:"StackFileContent"`
}
type Container struct {
ID string `json:"Id"`
Names []string `json:"Names"`
}
// FindContainer returns the container ID for a given name (e.g. "seerr").
// Docker container names have a leading slash, so "/seerr" matches "seerr".
func (c *Client) FindContainer(name string) (string, error) {
var containers []Container
if err := c.get(fmt.Sprintf("/api/endpoints/%d/docker/containers/json", c.endpointID), &containers); err != nil {
return "", err
}
want := "/" + name
for _, ct := range containers {
for _, n := range ct.Names {
if n == want {
return ct.ID, nil
}
}
}
return "", fmt.Errorf("container %q not found", name)
}
func New(baseURL, token, endpointName string) (*Client, error) {
c := &Client{
baseURL: strings.TrimRight(baseURL, "/"),