github.com/annwntech/go-micro/v2@v2.9.5/runtime/kubernetes/logs.go (about) 1 // Package kubernetes taken from https://github.com/micro/go-micro/blob/master/debug/log/kubernetes/kubernetes.go 2 // There are some modifications compared to the other files as 3 // this package doesn't provide write functionality. 4 // With the write functionality gone, structured logs also go away. 5 package kubernetes 6 7 import ( 8 "bufio" 9 "strconv" 10 "time" 11 12 "github.com/annwntech/go-micro/v2/runtime" 13 "github.com/annwntech/go-micro/v2/util/kubernetes/client" 14 "github.com/annwntech/go-micro/v2/util/log" 15 ) 16 17 type klog struct { 18 client client.Client 19 serviceName string 20 options runtime.LogsOptions 21 } 22 23 func (k *klog) podLogStream(podName string, stream *kubeStream) error { 24 p := make(map[string]string) 25 p["follow"] = "true" 26 27 opts := []client.LogOption{ 28 client.LogParams(p), 29 client.LogNamespace(k.options.Namespace), 30 } 31 32 // get the logs for the pod 33 body, err := k.client.Log(&client.Resource{ 34 Name: podName, 35 Kind: "pod", 36 }, opts...) 37 38 if err != nil { 39 stream.err = err 40 stream.Stop() 41 return err 42 } 43 44 s := bufio.NewScanner(body) 45 defer body.Close() 46 47 for { 48 select { 49 case <-stream.stop: 50 return stream.Error() 51 default: 52 if s.Scan() { 53 record := runtime.LogRecord{ 54 Message: s.Text(), 55 } 56 stream.stream <- record 57 } else { 58 // TODO: is there a blocking call 59 // rather than a sleep loop? 60 time.Sleep(time.Second) 61 } 62 } 63 } 64 } 65 66 func (k *klog) getMatchingPods() ([]string, error) { 67 r := &client.Resource{ 68 Kind: "pod", 69 Value: new(client.PodList), 70 } 71 72 l := make(map[string]string) 73 74 l["name"] = client.Format(k.serviceName) 75 // TODO: specify micro:service 76 // l["micro"] = "service" 77 78 opts := []client.GetOption{ 79 client.GetLabels(l), 80 client.GetNamespace(k.options.Namespace), 81 } 82 83 if err := k.client.Get(r, opts...); err != nil { 84 return nil, err 85 } 86 87 var matches []string 88 89 for _, p := range r.Value.(*client.PodList).Items { 90 // find labels that match the name 91 if p.Metadata.Labels["name"] == client.Format(k.serviceName) { 92 matches = append(matches, p.Metadata.Name) 93 } 94 } 95 96 return matches, nil 97 } 98 99 func (k *klog) Read() ([]runtime.LogRecord, error) { 100 pods, err := k.getMatchingPods() 101 if err != nil { 102 return nil, err 103 } 104 105 var records []runtime.LogRecord 106 107 for _, pod := range pods { 108 logParams := make(map[string]string) 109 110 //if !opts.Since.Equal(time.Time{}) { 111 // logParams["sinceSeconds"] = strconv.Itoa(int(time.Since(opts.Since).Seconds())) 112 //} 113 114 if k.options.Count != 0 { 115 logParams["tailLines"] = strconv.Itoa(int(k.options.Count)) 116 } 117 118 if k.options.Stream == true { 119 logParams["follow"] = "true" 120 } 121 122 opts := []client.LogOption{ 123 client.LogParams(logParams), 124 client.LogNamespace(k.options.Namespace), 125 } 126 127 logs, err := k.client.Log(&client.Resource{ 128 Name: pod, 129 Kind: "pod", 130 }, opts...) 131 132 if err != nil { 133 return nil, err 134 } 135 defer logs.Close() 136 137 s := bufio.NewScanner(logs) 138 139 for s.Scan() { 140 record := runtime.LogRecord{ 141 Message: s.Text(), 142 } 143 // record.Metadata["pod"] = pod 144 records = append(records, record) 145 } 146 } 147 148 // sort the records 149 // sort.Slice(records, func(i, j int) bool { return records[i].Timestamp.Before(records[j].Timestamp) }) 150 151 return records, nil 152 } 153 154 func (k *klog) Stream() (runtime.LogStream, error) { 155 // find the matching pods 156 pods, err := k.getMatchingPods() 157 if err != nil { 158 return nil, err 159 } 160 161 stream := &kubeStream{ 162 stream: make(chan runtime.LogRecord), 163 stop: make(chan bool), 164 } 165 166 // stream from the individual pods 167 for _, pod := range pods { 168 go func(podName string) { 169 err := k.podLogStream(podName, stream) 170 if err != nil { 171 log.Errorf("Error streaming from pod: %v", err) 172 } 173 }(pod) 174 } 175 176 return stream, nil 177 } 178 179 // NewLog returns a configured Kubernetes logger 180 func newLog(c client.Client, serviceName string, opts ...runtime.LogsOption) *klog { 181 options := runtime.LogsOptions{ 182 Namespace: client.DefaultNamespace, 183 } 184 for _, o := range opts { 185 o(&options) 186 } 187 188 klog := &klog{ 189 serviceName: serviceName, 190 client: c, 191 options: options, 192 } 193 194 return klog 195 }