Skip to content
This repository has been archived by the owner on Jun 25, 2024. It is now read-only.

feat: get realtime pod logs in a new tab #10

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
5 changes: 3 additions & 2 deletions cmd/ktop.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,9 @@ func (k *ktopCmd) run(cmd *cobra.Command, args []string) error {
termui.NewCol(1./2, logo),
termui.NewCol(1./2, hint),
),
termui.NewRow(6./12, monitor.GetPodTable()),
termui.NewRow(4./12,
termui.NewRow(3./12, monitor.GetPodTable()),
termui.NewRow(5./12, monitor.GetLogs()),
termui.NewRow(2./12,
termui.NewCol(1./2, monitor.GetCPUGraph()),
termui.NewCol(1./2, monitor.GetMemGraph()),
),
Expand Down
22 changes: 21 additions & 1 deletion pkg/ktop/ktop.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const (
type Monitor struct {
*kube.KubeClients

logs *ui.Paragraph
table *ui.Table
tableTypeCircle *ring.Ring

Expand Down Expand Up @@ -67,6 +68,14 @@ func NewMonitor(kubeclients *kube.KubeClients, podQuery, containerQuery, nodeQue
table.BorderStyle = termui.NewStyle(borderColor)
table.CursorColor = selectedTableColor

// logs of pod
logs := ui.NewParagraph()
logs.Title = "⎈ Logs ⎈"
logs.TitleStyle = titleStyle
logs.Text = `Loading...`
logs.BorderStyle = termui.NewStyle(borderColor)
logs.TextStyle = termui.NewStyle(termui.Color(244), termui.ColorClear)

// graph for cpu
cpu := ui.NewGraph()
cpu.Title = "⎈ CPU Usage ⎈"
Expand All @@ -86,6 +95,7 @@ func NewMonitor(kubeclients *kube.KubeClients, podQuery, containerQuery, nodeQue
mem.LimitColor = graphLimitColor

monitor.table = table
monitor.logs = logs
monitor.cpuGraph = cpu
monitor.memGraph = mem
return monitor
Expand Down Expand Up @@ -149,6 +159,10 @@ func (m *Monitor) GetPodTable() *ui.Table {
return m.table
}

func (m *Monitor) GetLogs() *ui.Paragraph {
return m.logs
}

func (m *Monitor) Update() error {
nodeList, err := m.GetNodeList(labels.Everything())
if err != nil {
Expand Down Expand Up @@ -277,6 +291,10 @@ func (m *Monitor) fetchPodResources() ([]*resource.Resource, []*resource.Summari
// filtered
for _, podMetrics := range FilterPodMetrics(m.podQuery, podMetricsList.Items) {
podName := podMetrics.Name
podLogs, err := m.GetPodLogs(*m.Flags.Namespace, podName)
if err != nil {
fmt.Print(err)
}
pod := FindPod(podName, podList.Items)
if pod == nil {
continue
Expand All @@ -297,7 +315,7 @@ func (m *Monitor) fetchPodResources() ([]*resource.Resource, []*resource.Summari
corev1.ResourceList{
corev1.ResourceCPU: cpu,
corev1.ResourceMemory: mem,
})
}, podLogs)
summarizedResources = append(summarizedResources, summarizedResource)
}
return resources, summarizedResources, nil
Expand Down Expand Up @@ -330,6 +348,8 @@ func (m *Monitor) updateSummarizedGraph(nodeList *corev1.NodeList, summarized *r
limitMemory := GetResourceValue(node.Status.Allocatable, corev1.ResourceMemory)
limitMemoryStr := GetResourceValueString(node.Status.Allocatable, corev1.ResourceMemory)

m.logs.Text = summarized.GetLogs()

m.cpuGraph.LabelHeader = fmt.Sprintf("Name: %v", summarized.GetPodName())
m.cpuGraph.Data = append(m.cpuGraph.Data, cpuUsage)
m.cpuGraph.LabelData = fmt.Sprintf("Usage: %v", cpuUsageStr)
Expand Down
36 changes: 36 additions & 0 deletions pkg/kube/clients.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package kube

import (
"io"

"github.com/pkg/errors"

corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -53,6 +55,40 @@ func (k *KubeClients) GetPodList(namespace string, labelSelector labels.Selector
return k.clientset.CoreV1().Pods(namespace).List(metav1.ListOptions{LabelSelector: labelSelector.String()})
}

func (k *KubeClients) GetPodLogs(namespace string, podName string) (string, error) {
// Tail size (number of lines)
count := int64(30)
follow := false
message := ""
podLogOptions := corev1.PodLogOptions{
Follow: follow,
TailLines: &count,
}
podLogRequest := k.clientset.CoreV1().
Pods(namespace).
GetLogs(podName, &podLogOptions)
stream, err := podLogRequest.Stream()
if err != nil {
return "failure1", err
}
defer stream.Close()
for {
buf := make([]byte, 2000)
numBytes, err := stream.Read(buf)
if err == io.EOF {
break
}
if err != nil {
return "failure2", err
}
if numBytes == 0 {
continue
}
message += string(buf[:numBytes])
}
return message, nil
}

func (k *KubeClients) GetPodMetricsList(namespace string, labelSelector labels.Selector) (*metrics.PodMetricsList, error) {
return k.metricsClient.getPodMetricsList(namespace, labelSelector)
}
Expand Down
8 changes: 7 additions & 1 deletion pkg/resource/summarized.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import (
type SummarizedResource struct {
podName string
nodeName string
logs string
usage corev1.ResourceList
}

func NewSummarizedResource(p corev1.Pod, sumUsage corev1.ResourceList) *SummarizedResource {
func NewSummarizedResource(p corev1.Pod, sumUsage corev1.ResourceList, logs string) *SummarizedResource {
return &SummarizedResource{
podName: p.Name,
nodeName: p.Spec.NodeName,
logs: logs,
usage: sumUsage,
}
}
Expand All @@ -33,6 +35,10 @@ func (s *SummarizedResource) GetCpuUsage() (float64, string) {
GetResourceValueString(s.usage, corev1.ResourceCPU)
}

func (s *SummarizedResource) GetLogs() string {
return s.logs
}

func (s *SummarizedResource) GetMemoryUsage() (float64, string) {
return GetResourceValue(s.usage, corev1.ResourceMemory),
GetResourceValueString(s.usage, corev1.ResourceMemory)
Expand Down
44 changes: 44 additions & 0 deletions pkg/ui/paragraph.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ui

import (
"image"

. "github.com/gizak/termui/v3"
)

type Paragraph struct {
Block
Text string
TextStyle Style
WrapText bool
}

func NewParagraph() *Paragraph {
return &Paragraph{
Block: *NewBlock(),
TextStyle: Theme.Paragraph.Text,
WrapText: true,
}
}

func (self *Paragraph) Draw(buf *Buffer) {
self.Block.Draw(buf)

cells := ParseStyles(self.Text, self.TextStyle)
if self.WrapText {
cells = WrapCells(cells, uint(self.Inner.Dx()))
}

rows := SplitCells(cells, '\n')

for y, row := range rows {
if y+self.Inner.Min.Y >= self.Inner.Max.Y {
break
}
row = TrimCells(row, self.Inner.Dx())
for _, cx := range BuildCellWithXArray(row) {
x, cell := cx.X, cx.Cell
buf.SetCell(cell, image.Pt(x, y).Add(self.Inner.Min))
}
}
}