add better logs
This commit is contained in:
@@ -4,24 +4,43 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var logsCmd = &cobra.Command{
|
var logsCmd = &cobra.Command{
|
||||||
Use: "logs",
|
Use: "logs [container]",
|
||||||
Short: "Open the Dozzle log viewer in the browser",
|
Short: "Open Dozzle in the browser, optionally deep-linking to a container",
|
||||||
|
Args: cobra.MaximumNArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
cfg, _, _, err := setup()
|
cfg, client, _, err := setup()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derive logs URL from Portainer URL (same base domain)
|
logsBase := deriveLogsURL(cfg.Portainer.URL)
|
||||||
// e.g. https://portainer.home.jens.pub → https://logs.home.jens.pub
|
|
||||||
logsURL := deriveLogsURL(cfg.Portainer.URL)
|
if len(args) == 0 {
|
||||||
fmt.Printf("Opening %s\n", logsURL)
|
fmt.Printf("Opening %s\n", logsBase)
|
||||||
return openBrowser(logsURL)
|
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 {
|
func deriveLogsURL(portainerURL string) string {
|
||||||
|
domain := stripScheme(portainerURL)
|
||||||
// Replace "portainer." prefix with "logs."
|
// 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 {
|
func stripScheme(u string) string {
|
||||||
for _, prefix := range []string{"https://", "http://"} {
|
for _, prefix := range []string{"https://", "http://"} {
|
||||||
if len(u) > len(prefix) && u[:len(prefix)] == prefix {
|
if strings.HasPrefix(u, prefix) {
|
||||||
return u[len(prefix):]
|
return u[len(prefix):]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,29 @@ type StackFile struct {
|
|||||||
StackFileContent string `json:"StackFileContent"`
|
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) {
|
func New(baseURL, token, endpointName string) (*Client, error) {
|
||||||
c := &Client{
|
c := &Client{
|
||||||
baseURL: strings.TrimRight(baseURL, "/"),
|
baseURL: strings.TrimRight(baseURL, "/"),
|
||||||
|
|||||||
Reference in New Issue
Block a user