Skip to content

Commit

Permalink
saving cli log output to support bundle
Browse files Browse the repository at this point in the history
  • Loading branch information
cxbrowne1207 committed Dec 5, 2022
1 parent 8e728f7 commit 9dfc2b8
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 4 deletions.
6 changes: 6 additions & 0 deletions pkg/diagnostics/collector_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type Collect struct {
ClusterResources *clusterResources `json:"clusterResources,omitempty"`
Secret *secret `json:"secret,omitempty"`
Logs *logs `json:"logs,omitempty"`
Data *data `json:"data,omitempty"`
CopyFromHost *copyFromHost `json:"copyFromHost,omitempty"`
Exec *exec `json:"exec,omitempty"`
RunPod *runPod `json:"runPod,omitempty"`
Expand Down Expand Up @@ -46,6 +47,11 @@ type logs struct {
Limits *logLimits `json:"limits,omitempty"`
}

type data struct {
Name string `json:"name,omitempty"`
Data string `json:"data,omitempty"`
}

type copyFromHost struct {
collectorMeta `json:",inline"`
Name string `json:"name,omitempty"`
Expand Down
15 changes: 14 additions & 1 deletion pkg/diagnostics/collectors.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package diagnostics

import (
"fmt"
"strings"
"time"

v1 "k8s.io/api/core/v1"

"github.com/aws/eks-anywhere/pkg/api/v1alpha1"
"github.com/aws/eks-anywhere/pkg/cluster"
"github.com/aws/eks-anywhere/pkg/constants"
"github.com/aws/eks-anywhere/pkg/logger"
"github.com/aws/eks-anywhere/pkg/providers"
)

Expand Down Expand Up @@ -43,7 +45,7 @@ func (c *collectorFactory) DefaultCollectors() []*Collect {
},
},
}
collectors = append(collectors, c.defaultLogCollectors()...)
collectors = append(collectors, append(c.defaultLogCollectors(), c.defaultStaticDataCollectors()...)...)
return collectors
}

Expand Down Expand Up @@ -212,6 +214,17 @@ func (c *collectorFactory) ubuntuHostCollectors() []*Collect {
}
}

func (c *collectorFactory) defaultStaticDataCollectors() []*Collect {
return []*Collect{
{
Data: &data{
Data: strings.Join(logger.GetLogs(9), "\n"),
Name: logpath("cli.log"),
},
},
}
}

func (c *collectorFactory) defaultLogCollectors() []*Collect {
return []*Collect{
{
Expand Down
2 changes: 2 additions & 0 deletions pkg/diagnostics/diagnostic_bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ func (e *EksaDiagnosticBundle) WriteBundleConfig() error {
if err != nil {
return err
}

logger.V(3).Info("bundle config written", "path", e.bundlePath)
return nil
}
Expand Down Expand Up @@ -243,6 +244,7 @@ func (e *EksaDiagnosticBundle) WriteAnalysisToFile() (path string, err error) {
}

func (e *EksaDiagnosticBundle) WithDefaultCollectors() *EksaDiagnosticBundle {
// Add static data collector as a default collector for cli logs
e.bundle.Spec.Collectors = append(e.bundle.Spec.Collectors, e.collectorFactory.DefaultCollectors()...)
return e
}
Expand Down
133 changes: 133 additions & 0 deletions pkg/logger/caching_logsink.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package logger

import (
"encoding/json"
"fmt"
"time"

"github.com/go-logr/logr"
)

type log struct {
timestamp string
level int
msg string
keyAndValues []interface{}
}

// CachingLogSink enhances the provided logger's implementation to save logs to memory.
type CachingLogSink struct {
logger logr.Logger
logs []log
}

func (m *CachingLogSink) getLogs(level int) []log {
filtered := []log{}

for _, log := range m.logs {
if log.level <= level {
filtered = append(filtered, log)
}
}

return filtered
}

func (l *log) keyAndValuesObject() map[string]string {
m := make(map[string]string)
for i := 0; i < len(l.keyAndValues); i += 2 {
strKey := fmt.Sprintf("%v", l.keyAndValues[i])
value := l.keyAndValues[i+1]
if value != nil || value == "<nil>" {
m[strKey] = "null"
} else {
m[strKey] = fmt.Sprintf("%v", value)
}

}

return m
}

func (l *log) keyAndValuesObjectStr() string {
m := l.keyAndValuesObject()
mJSON, err := json.Marshal(m)
if err != nil {
return fmt.Sprintf("%v", m)
}

return string(mJSON)
}

func formatLog(l log) string {
if l.level < 0 {
return fmt.Sprintf("%s \t %s", l.msg, l.keyAndValuesObjectStr())
}

return fmt.Sprintf("%s \t %s \t %s \t %s", l.timestamp, fmt.Sprintf("V%d", l.level), l.msg, l.keyAndValuesObjectStr())
}

// SetLogger assigns the provided Logger as a base for logging.
func (m *CachingLogSink) SetLogger(l logr.Logger) {
m.logger = l
}

func (m *CachingLogSink) save(level int, msg string, keysAndValues ...interface{}) {
timestamp := time.Now().Format("2006-01-02T15:04:05.000-0600")
m.logs = append(m.logs, log{timestamp, level, msg, keysAndValues})
}

// Init receives optional information about the logr library for LogSink
// implementations that need it.
func (m *CachingLogSink) Init(info logr.RuntimeInfo) {
m.logger.GetSink().Init(info)
}

// Enabled tests whether this LogSink is enabled at the specified V-level.
// For example, commandline flags might be used to set the logging
// verbosity and disable some info logs.
func (m *CachingLogSink) Enabled(level int) bool {
return m.logger.GetSink().Enabled(level)
}

// Info logs a non-error message with the given key/value pairs as context.
//
// The msg argument should be used to add some constant description to
// the log line. The key/value pairs can then be used to add additional
// variable information. The key/value pairs should alternate string
// keys and arbitrary values.
func (m *CachingLogSink) Info(level int, msg string, keysAndValues ...interface{}) {
m.logger.GetSink().Info(level, msg, keysAndValues...)
m.save(level, msg, keysAndValues...)
}

// Error logs an error, with the given message and key/value pairs as
// context. See Logger.Error for more details.
func (m *CachingLogSink) Error(err error, msg string, keysAndValues ...interface{}) {
m.logger.GetSink().Error(err, msg, keysAndValues...)
m.save(-1, msg, append(keysAndValues, err)...)
}

// WithValues returns a new LogSink with additional key/value pairs. See
// Logger.WithValues for more details.
func (m *CachingLogSink) WithValues(keysAndValues ...interface{}) logr.LogSink {
return m.logger.GetSink().WithValues(keysAndValues...)
}

// WithName returns a new LogSink with the specified name appended. See
// Logger.WithName for more details.
func (m *CachingLogSink) WithName(name string) logr.LogSink {
return m.logger.GetSink().WithName(name)
}

// GetLogs returns the logs stored in memory logged from the registered Logger.
func (m *CachingLogSink) GetLogs(level int) []string {
formatted := []string{}
for _, log := range m.getLogs(level) {
if log.level <= level {
formatted = append(formatted, formatLog(log))
}
}

return formatted
}
13 changes: 10 additions & 3 deletions pkg/logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,23 @@ const (
)

var (
l logr.Logger = logr.Discard()
once sync.Once
l logr.Logger = logr.Discard()
cachingLogSink = CachingLogSink{logger: l}
once sync.Once
)

func set(logger logr.Logger) {
once.Do(func() {
l = logger
cachingLogSink.SetLogger(logger)
l = logr.New(&cachingLogSink)
})
}

// Get Logs returns the stored in memory logs based on the provided verbosity level.
func GetLogs(level int) []string {
return cachingLogSink.GetLogs(level)
}

// Get returns the logger instance that has been previously set.
// If no logger has been set, it returns a null logger.
func Get() logr.Logger {
Expand Down

0 comments on commit 9dfc2b8

Please sign in to comment.