mirror of
				https://github.com/NVIDIA/nvidia-container-toolkit
				synced 2025-06-26 18:18:24 +00:00 
			
		
		
		
	Support runc logging command line options
This change processes and supports runc logging command line arguments. This allows for better integration into container engines such as docker. Signed-off-by: Evan Lezar <elezar@nvidia.com>
This commit is contained in:
		
							parent
							
								
									e93bafa6d4
								
							
						
					
					
						commit
						e9d929dc2f
					
				| @ -20,60 +20,205 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"runtime" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/sirupsen/logrus" | 	"github.com/sirupsen/logrus" | ||||||
| 	"github.com/tsaikd/KDGoLib/logrusutil" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Logger adds a way to manage output to a log file to a logrus.Logger
 | // Logger adds a way to manage output to a log file to a logrus.Logger
 | ||||||
| type Logger struct { | type Logger struct { | ||||||
| 	*logrus.Logger | 	*logrus.Logger | ||||||
| 	previousOutput io.Writer | 	previousLogger *logrus.Logger | ||||||
| 	logFile        *os.File | 	logFiles       []*os.File | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewLogger constructs a Logger with a preddefined formatter
 | // NewLogger creates an empty logger
 | ||||||
| func NewLogger() *Logger { | func NewLogger() *Logger { | ||||||
| 	logrusLogger := logrus.New() | 	return &Logger{ | ||||||
| 
 | 		Logger: logrus.New(), | ||||||
| 	formatter := &logrusutil.ConsoleLogFormatter{ |  | ||||||
| 		TimestampFormat: "2006/01/02 15:04:07", |  | ||||||
| 		Flag:            logrusutil.Ltime, |  | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	logger := &Logger{ |  | ||||||
| 		Logger: logrusLogger, |  | ||||||
| 	} |  | ||||||
| 	logger.SetFormatter(formatter) |  | ||||||
| 
 |  | ||||||
| 	return logger |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // LogToFile opens the specified file for appending and sets the logger to
 | // UpdateLogger constructs a Logger with a preddefined formatter
 | ||||||
| // output to the opened file. A reference to the file pointer is stored to
 | func UpdateLogger(filename string, logLevel string, argv []string) (*Logger, error) { | ||||||
| // allow this to be closed.
 | 	configFromArgs := parseArgs(argv) | ||||||
| func (l *Logger) LogToFile(filename string) error { | 
 | ||||||
| 	logFile, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) | 	level, logLevelError := configFromArgs.getLevel(logLevel) | ||||||
|  | 
 | ||||||
|  | 	var logFiles []*os.File | ||||||
|  | 
 | ||||||
|  | 	configLogFile, err := createLogFile(filename) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("error opening debug log file: %v", err) | 		return logger, fmt.Errorf("error opening debug log file: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if configLogFile != nil { | ||||||
|  | 		logFiles = append(logFiles, configLogFile) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	l.logFile = logFile | 	argLogFile, argLogFileError := createLogFile(configFromArgs.file) | ||||||
| 	l.previousOutput = l.Out | 	if argLogFile != nil { | ||||||
| 	l.SetOutput(logFile) | 		logFiles = append(logFiles, argLogFile) | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	return nil | 	l := &Logger{ | ||||||
|  | 		Logger:         logrus.New(), | ||||||
|  | 		previousLogger: logger.Logger, | ||||||
|  | 		logFiles:       logFiles, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(logFiles) == 0 { | ||||||
|  | 		l.SetOutput(io.Discard) | ||||||
|  | 	} else if len(logFiles) == 1 { | ||||||
|  | 		l.SetOutput(logFiles[0]) | ||||||
|  | 	} else { | ||||||
|  | 		var writers []io.Writer | ||||||
|  | 		for _, f := range logFiles { | ||||||
|  | 			writers = append(writers, f) | ||||||
|  | 		} | ||||||
|  | 		l.SetOutput(io.MultiWriter(writers...)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	l.SetLevel(level) | ||||||
|  | 	if level == logrus.DebugLevel { | ||||||
|  | 		logrus.SetReportCaller(true) | ||||||
|  | 		// Shorten function and file names reported by the logger, by
 | ||||||
|  | 		// trimming common "github.com/opencontainers/runc" prefix.
 | ||||||
|  | 		// This is only done for text formatter.
 | ||||||
|  | 		_, file, _, _ := runtime.Caller(0) | ||||||
|  | 		prefix := filepath.Dir(file) + "/" | ||||||
|  | 		logrus.SetFormatter(&logrus.TextFormatter{ | ||||||
|  | 			CallerPrettyfier: func(f *runtime.Frame) (string, string) { | ||||||
|  | 				function := strings.TrimPrefix(f.Function, prefix) + "()" | ||||||
|  | 				fileLine := strings.TrimPrefix(f.File, prefix) + ":" + strconv.Itoa(f.Line) | ||||||
|  | 				return function, fileLine | ||||||
|  | 			}, | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if configFromArgs.format == "json" { | ||||||
|  | 		l.SetFormatter(new(logrus.JSONFormatter)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if logLevelError != nil { | ||||||
|  | 		l.Warn(logLevelError) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if argLogFileError != nil { | ||||||
|  | 		l.Warnf("Failed to open log file: %v", argLogFileError) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return l, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // CloseFile closes the log file (if any) and resets the logger output to what it
 | // CloseFile closes the log file (if any) and resets the logger output to what it
 | ||||||
| // was before LogToFile was called.
 | // was before UpdateLogger was called.
 | ||||||
| func (l *Logger) CloseFile() error { | func (l *Logger) CloseFile() error { | ||||||
| 	if l.logFile == nil { | 	defer func() { | ||||||
| 		return nil | 		previous := l.previousLogger | ||||||
| 	} | 		if previous == nil { | ||||||
| 	logFile := l.logFile | 			previous = logrus.New() | ||||||
| 	l.SetOutput(l.previousOutput) | 		} | ||||||
| 	l.logFile = nil | 		logger = &Logger{Logger: previous} | ||||||
|  | 	}() | ||||||
| 
 | 
 | ||||||
| 	return logFile.Close() | 	var errs []error | ||||||
|  | 	for _, f := range l.logFiles { | ||||||
|  | 		err := f.Close() | ||||||
|  | 		if err != nil { | ||||||
|  | 			errs = append(errs, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var err error | ||||||
|  | 	for _, e := range errs { | ||||||
|  | 		if err == nil { | ||||||
|  | 			err = e | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		return fmt.Errorf("%v; %w", e, err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func createLogFile(filename string) (*os.File, error) { | ||||||
|  | 	if filename != "" && filename != os.DevNull { | ||||||
|  | 		return os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type loggerConfig struct { | ||||||
|  | 	file   string | ||||||
|  | 	format string | ||||||
|  | 	debug  bool | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c loggerConfig) getLevel(logLevel string) (logrus.Level, error) { | ||||||
|  | 	if c.debug { | ||||||
|  | 		return logrus.DebugLevel, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if logLevel, err := logrus.ParseLevel(logLevel); err == nil { | ||||||
|  | 		return logLevel, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return logrus.InfoLevel, fmt.Errorf("invalid log-level '%v'", logLevel) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Informed by Taken from https://github.com/opencontainers/runc/blob/7fd8b57001f5bfa102e89cb434d96bf71f7c1d35/main.go#L182
 | ||||||
|  | func parseArgs(args []string) loggerConfig { | ||||||
|  | 	c := loggerConfig{} | ||||||
|  | 
 | ||||||
|  | 	expected := map[string]*string{ | ||||||
|  | 		"log-format": &c.format, | ||||||
|  | 		"log":        &c.file, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	found := make(map[string]bool) | ||||||
|  | 
 | ||||||
|  | 	for i := 0; i < len(args); i++ { | ||||||
|  | 		if len(found) == 3 { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		param := args[i] | ||||||
|  | 
 | ||||||
|  | 		parts := strings.SplitN(param, "=", 2) | ||||||
|  | 		trimmed := strings.TrimPrefix(parts[0], "--") | ||||||
|  | 		if !strings.HasPrefix(parts[0], "--") { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Check the debug flag
 | ||||||
|  | 		if trimmed == "debug" { | ||||||
|  | 			c.debug = true | ||||||
|  | 			found["debug"] = true | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		destination, exists := expected[trimmed] | ||||||
|  | 		if !exists { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		var value string | ||||||
|  | 		if len(parts) == 2 { | ||||||
|  | 			value = parts[2] | ||||||
|  | 		} else if i+1 < len(args) { | ||||||
|  | 			value = args[i+1] | ||||||
|  | 			i++ | ||||||
|  | 		} else { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		*destination = value | ||||||
|  | 		found[trimmed] = true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return c | ||||||
| } | } | ||||||
|  | |||||||
| @ -5,7 +5,6 @@ import ( | |||||||
| 	"os" | 	"os" | ||||||
| 
 | 
 | ||||||
| 	"github.com/NVIDIA/nvidia-container-toolkit/internal/config" | 	"github.com/NVIDIA/nvidia-container-toolkit/internal/config" | ||||||
| 	"github.com/sirupsen/logrus" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var logger = NewLogger() | var logger = NewLogger() | ||||||
| @ -26,22 +25,13 @@ func run(argv []string) (rerr error) { | |||||||
| 		return fmt.Errorf("error loading config: %v", err) | 		return fmt.Errorf("error loading config: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = logger.LogToFile(cfg.NVIDIAContainerRuntimeConfig.DebugFilePath) | 	logger, err = UpdateLogger( | ||||||
|  | 		cfg.NVIDIAContainerRuntimeConfig.DebugFilePath, | ||||||
|  | 		cfg.NVIDIAContainerRuntimeConfig.LogLevel, | ||||||
|  | 		argv, | ||||||
|  | 	) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("error opening debug log file: %v", err) | 		return fmt.Errorf("failed to set up logger: %v", err) | ||||||
| 	} |  | ||||||
| 	defer func() { |  | ||||||
| 		// We capture and log a returning error before closing the log file.
 |  | ||||||
| 		if rerr != nil { |  | ||||||
| 			logger.Errorf("%v", rerr) |  | ||||||
| 		} |  | ||||||
| 		logger.CloseFile() |  | ||||||
| 	}() |  | ||||||
| 
 |  | ||||||
| 	if logLevel, err := logrus.ParseLevel(cfg.NVIDIAContainerRuntimeConfig.LogLevel); err == nil { |  | ||||||
| 		logger.SetLevel(logLevel) |  | ||||||
| 	} else { |  | ||||||
| 		logger.Warnf("Invalid log-level '%v'; using '%v'", cfg.NVIDIAContainerRuntimeConfig.LogLevel, logger.Level.String()) |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	logger.Debugf("Command line arguments: %v", argv) | 	logger.Debugf("Command line arguments: %v", argv) | ||||||
|  | |||||||
| @ -61,7 +61,7 @@ func NewExperimentalModifier(logger *logrus.Logger, cfg *config.Config, ociSpec | |||||||
| 		logger.Infof("No modification required: %v=%v (exists=%v)", visibleDevicesEnvvar, visibleDevices, exists) | 		logger.Infof("No modification required: %v=%v (exists=%v)", visibleDevicesEnvvar, visibleDevices, exists) | ||||||
| 		return nil, nil | 		return nil, nil | ||||||
| 	} | 	} | ||||||
| 	logger.Infof("Constructing modifier from config: %+v", cfg) | 	logger.Infof("Constructing modifier from config: %+v", *cfg) | ||||||
| 
 | 
 | ||||||
| 	config := &discover.Config{ | 	config := &discover.Config{ | ||||||
| 		Root:                                    cfg.NVIDIAContainerCLIConfig.Root, | 		Root:                                    cfg.NVIDIAContainerCLIConfig.Root, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user