-
Notifications
You must be signed in to change notification settings - Fork 208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Reading Logs in daignose cli command #1521
Changes from 2 commits
07d51c9
78100f5
2956a2d
39cc275
74fb66f
6f682bc
788ec90
ef6b7ff
125398f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
package cmd | ||
|
||
import ( | ||
"compress/gzip" | ||
"context" | ||
"fmt" | ||
"github.com/odigos-io/odigos/cli/cmd/resources" | ||
"github.com/odigos-io/odigos/cli/pkg/kube" | ||
"github.com/spf13/cobra" | ||
"io" | ||
v1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"os" | ||
"path/filepath" | ||
) | ||
|
||
const ( | ||
mainDir = "odigos-diagnose" | ||
logDir = "logs" | ||
logBufferSize = 1024 * 1024 // 1MB buffer size for reading logs in chunks | ||
) | ||
|
||
var diagnozeCmd = &cobra.Command{ | ||
Use: "diagnose", | ||
Short: "Diagnose Client Cluster", | ||
Long: `Diagnose Client Cluster to identify issues and resolve them. This command is useful for troubleshooting and debugging.`, | ||
Run: func(cmd *cobra.Command, args []string) { | ||
ctx := cmd.Context() | ||
client, err := kube.CreateClient(cmd) | ||
if err != nil { | ||
kube.PrintClientErrorAndExit(err) | ||
} | ||
|
||
err = startDiagnose(ctx, client) | ||
if err != nil { | ||
fmt.Printf("The diagnose script crashed on: %v\n", err) | ||
} | ||
}, | ||
} | ||
|
||
func startDiagnose(ctx context.Context, client *kube.Client) error { | ||
if err := createAllDirs(); err != nil { | ||
return err | ||
} | ||
|
||
if err := fetchOdigosComponentsLogs(ctx, client); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func createAllDirs() error { | ||
if err := os.RemoveAll(mainDir); err != nil { | ||
return err | ||
} | ||
if err := os.MkdirAll(mainDir, 0755); err != nil { | ||
yodigos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return err | ||
} | ||
|
||
logsPath := filepath.Join(mainDir, logDir) | ||
if err := os.MkdirAll(logsPath, 0755); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func fetchOdigosComponentsLogs(ctx context.Context, client *kube.Client) error { | ||
odigosNamespace, err := resources.GetOdigosNamespace(client, ctx) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
pods, err := client.CoreV1().Pods(odigosNamespace).List(context.TODO(), metav1.ListOptions{}) | ||
yodigos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, pod := range pods.Items { | ||
if err = fetchPodLogs(ctx, client, odigosNamespace, pod); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func fetchPodLogs(ctx context.Context, client *kube.Client, odigosNamespace string, pod v1.Pod) error { | ||
for _, container := range pod.Spec.Containers { | ||
fmt.Printf("Fetching logs for Pod: %s, Container: %s\n", pod.Name, container.Name) | ||
yodigos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Define the log file path for saving compressed logs | ||
logFilePath := filepath.Join(mainDir, logDir, pod.Name+"_"+container.Name+".log.gz") | ||
logFile, err := os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should add here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why Not Use defer Inside the Loop? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok I read a bit and this is a real cool way to solve this.. The anonymous function will be called after opening the file and because it uses defer and created inside the scope of the for-loop -> it will trigger in the end of the loop (succesfully / with error)
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can also extract the code in the loop body to a function and use a defer within that function, that way there is no risc for a long queue of defer functions. |
||
if err != nil { | ||
return err | ||
yodigos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
req := client.CoreV1().Pods(odigosNamespace).GetLogs(pod.Name, &v1.PodLogOptions{}) | ||
logStream, err := req.Stream(ctx) | ||
yodigos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
if err = saveLogsToGzipFileInBatches(logFile, logStream, logBufferSize); err != nil { | ||
return err | ||
} | ||
|
||
logStream.Close() | ||
logFile.Close() | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func saveLogsToGzipFileInBatches(logFile *os.File, logStream io.ReadCloser, bufferSize int) error { | ||
// Create a gzip writer to compress the logs | ||
gzipWriter := gzip.NewWriter(logFile) | ||
defer gzipWriter.Close() | ||
|
||
// Read logs in chunks and write them to the file | ||
buffer := make([]byte, bufferSize) | ||
for { | ||
n, err := logStream.Read(buffer) | ||
if n > 0 { | ||
// Write the chunk to the gzip file | ||
if _, err := gzipWriter.Write(buffer[:n]); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
if err == io.EOF { | ||
// End of the log stream; break out of the loop | ||
break | ||
} | ||
|
||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(diagnozeCmd) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also related to Eden's comment, you can have a look at the TempDir function.
I think having the files created in a temp location and only saving the final zip in the CWD is the accepted behavior
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TempDir is deprecated from go 1.16 - the new way is with os.MkdirTemp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ohhh my bad, ioutil.TempDir() is deprecated