Cleanup k8s logs
This commit is contained in:
		| @@ -20,6 +20,78 @@ type klog struct { | ||||
| 	log.Options | ||||
| } | ||||
|  | ||||
| func (k *klog) podLogStream(podName string, stream *kubeStream) { | ||||
| 	p := make(map[string]string) | ||||
| 	p["follow"] = "true" | ||||
|  | ||||
| 	body, err := k.client.Logs(podName, client.AdditionalParams(p)) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	s := bufio.NewScanner(body) | ||||
| 	defer body.Close() | ||||
|  | ||||
| 	for { | ||||
| 		select { | ||||
| 		case <-stream.stop: | ||||
| 			return | ||||
| 		default: | ||||
| 			if s.Scan() { | ||||
| 				record := k.parse(s.Text()) | ||||
| 				stream.stream <- record | ||||
| 			} else { | ||||
| 				// TODO: is there a blocking call | ||||
| 				// rather than a sleep loop? | ||||
| 				time.Sleep(time.Second) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (k *klog) getMatchingPods() ([]string, error) { | ||||
| 	r := &client.Resource{ | ||||
| 		Kind:  "pod", | ||||
| 		Value: new(client.PodList), | ||||
| 	} | ||||
|  | ||||
| 	l := make(map[string]string) | ||||
|  | ||||
| 	l["name"] = client.Format(k.Options.Name) | ||||
| 	// TODO: specify micro:service | ||||
| 	// l["micro"] = "service" | ||||
|  | ||||
| 	if err := k.client.Get(r, l); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var matches []string | ||||
|  | ||||
| 	for _, p := range r.Value.(*client.PodList).Items { | ||||
| 		// find labels that match the name | ||||
| 		if p.Metadata.Labels["name"] == client.Format(k.Options.Name) { | ||||
| 			matches = append(matches, p.Metadata.Name) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return matches, nil | ||||
| } | ||||
|  | ||||
| func (k *klog) parse(line string) log.Record { | ||||
| 	record := log.Record{} | ||||
|  | ||||
| 	if err := json.Unmarshal([]byte(line), &record); err != nil { | ||||
| 		record.Timestamp = time.Now().UTC() | ||||
| 		record.Message = line | ||||
| 		record.Metadata = make(map[string]string) | ||||
| 	} | ||||
|  | ||||
| 	record.Metadata["service"] = k.Options.Name | ||||
|  | ||||
| 	return record | ||||
| } | ||||
|  | ||||
| func (k *klog) Read(options ...log.ReadOption) ([]log.Record, error) { | ||||
| 	opts := &log.ReadOptions{} | ||||
| 	for _, o := range options { | ||||
| @@ -30,25 +102,32 @@ func (k *klog) Read(options ...log.ReadOption) ([]log.Record, error) { | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	records := []log.Record{} | ||||
|  | ||||
| 	var records []log.Record | ||||
|  | ||||
| 	for _, l := range logsToGet { | ||||
| 		logParams := make(map[string]string) | ||||
|  | ||||
| 		if !opts.Since.Equal(time.Time{}) { | ||||
| 			logParams["sinceSeconds"] = strconv.Itoa(int(time.Since(opts.Since).Seconds())) | ||||
| 		} | ||||
|  | ||||
| 		if opts.Count != 0 { | ||||
| 			logParams["tailLines"] = strconv.Itoa(opts.Count) | ||||
| 		} | ||||
|  | ||||
| 		if opts.Stream == true { | ||||
| 			logParams["follow"] = "true" | ||||
| 		} | ||||
|  | ||||
| 		logs, err := k.client.Logs(l, client.AdditionalParams(logParams)) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		defer logs.Close() | ||||
|  | ||||
| 		s := bufio.NewScanner(logs) | ||||
|  | ||||
| 		for s.Scan() { | ||||
| 			record := k.parse(s.Text()) | ||||
| 			record.Metadata["pod"] = l | ||||
| @@ -56,7 +135,9 @@ func (k *klog) Read(options ...log.ReadOption) ([]log.Record, error) { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	sort.Sort(byTimestamp(records)) | ||||
| 	// sort the records | ||||
| 	sort.Slice(records, func(i, j int) bool { return records[i].Timestamp.Before(records[j].Timestamp) }) | ||||
|  | ||||
| 	return records, nil | ||||
| } | ||||
|  | ||||
| @@ -65,58 +146,23 @@ func (k *klog) Write(l log.Record) error { | ||||
| } | ||||
|  | ||||
| func (k *klog) Stream() (log.Stream, error) { | ||||
| 	return k.stream() | ||||
| } | ||||
|  | ||||
| func (k *klog) stream() (log.Stream, error) { | ||||
| 	// find the matching pods | ||||
| 	pods, err := k.getMatchingPods() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	logStreamer := &klogStreamer{ | ||||
| 		streamChan: make(chan log.Record), | ||||
| 		stop:       make(chan bool), | ||||
| 	} | ||||
| 	errorChan := make(chan error) | ||||
| 	go func(stopChan <-chan bool) { | ||||
| 		for { | ||||
| 			select { | ||||
| 			case <-stopChan: | ||||
| 				return | ||||
| 			case err := <-errorChan: | ||||
| 				fmt.Fprintf(os.Stderr, err.Error()) | ||||
| 			} | ||||
| 		} | ||||
| 	}(logStreamer.stop) | ||||
| 	for _, pod := range pods { | ||||
| 		go k.individualPodLogStreamer(pod, logStreamer.streamChan, errorChan, logStreamer.stop) | ||||
| 	} | ||||
| 	return logStreamer, nil | ||||
| } | ||||
|  | ||||
| func (k *klog) individualPodLogStreamer(podName string, recordChan chan<- log.Record, errorChan chan<- error, stopChan <-chan bool) { | ||||
| 	p := make(map[string]string) | ||||
| 	p["follow"] = "true" | ||||
| 	body, err := k.client.Logs(podName, client.AdditionalParams(p)) | ||||
| 	if err != nil { | ||||
| 		errorChan <- err | ||||
| 		return | ||||
| 	stream := &kubeStream{ | ||||
| 		stream: make(chan log.Record), | ||||
| 		stop:   make(chan bool), | ||||
| 	} | ||||
| 	s := bufio.NewScanner(body) | ||||
| 	defer body.Close() | ||||
| 	for { | ||||
| 		select { | ||||
| 		case <-stopChan: | ||||
| 			return | ||||
| 		default: | ||||
| 			if s.Scan() { | ||||
| 				record := k.parse(s.Text()) | ||||
| 				recordChan <- record | ||||
| 			} else { | ||||
| 				time.Sleep(time.Second) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 	// stream from the individual pods | ||||
| 	for _, pod := range pods { | ||||
| 		go k.podLogStream(pod, stream) | ||||
| 	} | ||||
|  | ||||
| 	return stream, nil | ||||
| } | ||||
|  | ||||
| // New returns a configured Kubernetes logger | ||||
| @@ -133,34 +179,3 @@ func New(opts ...log.Option) log.Log { | ||||
| 	} | ||||
| 	return klog | ||||
| } | ||||
|  | ||||
| func (k *klog) getMatchingPods() ([]string, error) { | ||||
| 	r := &client.Resource{ | ||||
| 		Kind:  "pod", | ||||
| 		Value: new(client.PodList), | ||||
| 	} | ||||
| 	l := make(map[string]string) | ||||
| 	l["micro"] = "runtime" | ||||
| 	if err := k.client.Get(r, l); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var matches []string | ||||
| 	for _, p := range r.Value.(*client.PodList).Items { | ||||
| 		if p.Metadata.Labels["name"] == k.Options.Name { | ||||
| 			matches = append(matches, p.Metadata.Name) | ||||
| 		} | ||||
| 	} | ||||
| 	return matches, nil | ||||
| } | ||||
|  | ||||
| func (k *klog) parse(line string) log.Record { | ||||
| 	record := log.Record{} | ||||
| 	if err := json.Unmarshal([]byte(line), &record); err != nil { | ||||
| 		record.Timestamp = time.Now().UTC() | ||||
| 		record.Message = line | ||||
| 		record.Metadata = make(map[string]string) | ||||
| 	} | ||||
| 	record.Metadata["service"] = k.Options.Name | ||||
| 	return record | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user