Skip to main content
Glama
logs.go6.15 kB
package kiali import ( "context" "encoding/json" "fmt" "net/http" "net/url" "strings" ) // WorkloadLogs returns logs for a specific workload's pods in a namespace. // This method first gets workload details to find associated pods, then retrieves logs for each pod. // Parameters: // - namespace: the namespace containing the workload // - workload: the name of the workload // - container: container name (optional, will be auto-detected if not provided) // - service: service name (optional) // - duration: time duration (e.g., "5m", "1h") - optional // - logType: type of logs (app, proxy, ztunnel, waypoint) - optional // - sinceTime: Unix timestamp for start time - optional // - maxLines: maximum number of lines to return - optional func (k *Kiali) WorkloadLogs(ctx context.Context, namespace string, workload string, container string, service string, duration string, logType string, sinceTime string, maxLines string) (string, error) { if namespace == "" { return "", fmt.Errorf("namespace is required") } if workload == "" { return "", fmt.Errorf("workload name is required") } // Container is optional - will be auto-detected if not provided // First, get workload details to find associated pods workloadDetails, err := k.WorkloadDetails(ctx, namespace, workload) if err != nil { return "", fmt.Errorf("failed to get workload details: %v", err) } // Parse the workload details JSON to extract pod names and containers var workloadData struct { Pods []struct { Name string `json:"name"` Containers []struct { Name string `json:"name"` } `json:"containers"` } `json:"pods"` } if err := json.Unmarshal([]byte(workloadDetails), &workloadData); err != nil { return "", fmt.Errorf("failed to parse workload details: %v", err) } if len(workloadData.Pods) == 0 { return "", fmt.Errorf("no pods found for workload %s in namespace %s", workload, namespace) } // Collect logs from all pods var allLogs []string for _, pod := range workloadData.Pods { // Auto-detect container if not provided podContainer := container if podContainer == "" { // Find the main application container (not istio-proxy or istio-init) for _, c := range pod.Containers { if c.Name != "istio-proxy" && c.Name != "istio-init" { podContainer = c.Name break } } // If no app container found, use the first container if podContainer == "" && len(pod.Containers) > 0 { podContainer = pod.Containers[0].Name } } if podContainer == "" { allLogs = append(allLogs, fmt.Sprintf("Error: No container found for pod %s", pod.Name)) continue } podLogs, err := k.PodLogs(ctx, namespace, pod.Name, podContainer, workload, service, duration, logType, sinceTime, maxLines) if err != nil { // Log the error but continue with other pods allLogs = append(allLogs, fmt.Sprintf("Error getting logs for pod %s: %v", pod.Name, err)) continue } if podLogs != "" { allLogs = append(allLogs, fmt.Sprintf("=== Pod: %s (Container: %s) ===\n%s", pod.Name, podContainer, podLogs)) } } if len(allLogs) == 0 { return "", fmt.Errorf("no logs found for workload %s in namespace %s", workload, namespace) } return strings.Join(allLogs, "\n\n"), nil } // PodLogs returns logs for a specific pod using the Kiali API endpoint. // Parameters: // - namespace: the namespace containing the pod // - podName: the name of the pod // - container: container name (optional, will be auto-detected if not provided) // - workload: workload name (optional) // - service: service name (optional) // - duration: time duration (e.g., "5m", "1h") - optional // - logType: type of logs (app, proxy, ztunnel, waypoint) - optional // - sinceTime: Unix timestamp for start time - optional // - maxLines: maximum number of lines to return - optional func (k *Kiali) PodLogs(ctx context.Context, namespace string, podName string, container string, workload string, service string, duration string, logType string, sinceTime string, maxLines string) (string, error) { if namespace == "" { return "", fmt.Errorf("namespace is required") } if podName == "" { return "", fmt.Errorf("pod name is required") } // Container is optional - will be auto-detected if not provided podContainer := container if podContainer == "" { // Get pod details to find containers podDetails, err := k.executeRequest(ctx, http.MethodGet, fmt.Sprintf(PodDetailsEndpoint, url.PathEscape(namespace), url.PathEscape(podName)), "", nil) if err != nil { return "", fmt.Errorf("failed to get pod details: %v", err) } // Parse pod details to extract container names var podData struct { Containers []struct { Name string `json:"name"` } `json:"containers"` } if err := json.Unmarshal([]byte(podDetails), &podData); err != nil { return "", fmt.Errorf("failed to parse pod details: %v", err) } // Find the main application container (not istio-proxy or istio-init) for _, c := range podData.Containers { if c.Name != "istio-proxy" && c.Name != "istio-init" { podContainer = c.Name break } } // If no app container found, use the first container if podContainer == "" && len(podData.Containers) > 0 { podContainer = podData.Containers[0].Name } if podContainer == "" { return "", fmt.Errorf("no container found for pod %s in namespace %s", podName, namespace) } } endpoint := fmt.Sprintf(PodsLogsEndpoint, url.PathEscape(namespace), url.PathEscape(podName)) // Add query parameters u, err := url.Parse(endpoint) if err != nil { return "", err } q := u.Query() // Required parameters q.Set("container", podContainer) // Optional parameters if workload != "" { q.Set("workload", workload) } if service != "" { q.Set("service", service) } if duration != "" { q.Set("duration", duration) } if logType != "" { q.Set("logType", logType) } if sinceTime != "" { q.Set("sinceTime", sinceTime) } if maxLines != "" { q.Set("maxLines", maxLines) } u.RawQuery = q.Encode() endpoint = u.String() return k.executeRequest(ctx, http.MethodGet, endpoint, "", nil) }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/containers/kubernetes-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server