mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-05 23:51:25 +08:00
fix: optimize report command to avoid creating timestamp directories
- Implement lazy loading for directory creation in config.go - Add logFile parameter to InitLogger for better control - Use dynamic directory existence check instead of flags - Report command now uses console-only logging to prevent directory creation - Support both JSON and colorized console output formats - Maintain backward compatibility for all other commands Changes: - config.go: Convert directory paths to getter methods with lazy creation - logger.go: Add logFile parameter and improve logging control - cmd/root.go: Detect report command and disable file logging - uixt/*: Update all references to use new getter methods Fixes the issue where 'hrp report results/' would create unwanted timestamp directories
This commit is contained in:
+3
-1
@@ -46,7 +46,9 @@ GitHub: https://github.com/httprunner/httprunner
|
|||||||
|
|
||||||
Copyright © 2017-present debugtalk. Apache-2.0 License.`,
|
Copyright © 2017-present debugtalk. Apache-2.0 License.`,
|
||||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
hrp.InitLogger(logLevel, logJSON)
|
// For report command, don't create log files to avoid creating directories
|
||||||
|
enableLogFile := cmd.Name() != "report"
|
||||||
|
hrp.InitLogger(logLevel, logJSON, enableLogFile)
|
||||||
},
|
},
|
||||||
Version: version.GetVersionInfo(),
|
Version: version.GetVersionInfo(),
|
||||||
TraverseChildren: true, // parses flags on all parents before executing child command
|
TraverseChildren: true, // parses flags on all parents before executing child command
|
||||||
|
|||||||
+51
-17
@@ -21,12 +21,13 @@ const (
|
|||||||
type Config struct {
|
type Config struct {
|
||||||
RootDir string
|
RootDir string
|
||||||
ResultsDir string
|
ResultsDir string
|
||||||
ResultsPath string
|
resultsPath string
|
||||||
DownloadsPath string
|
downloadsPath string
|
||||||
ScreenShotsPath string
|
screenShotsPath string
|
||||||
StartTime time.Time
|
StartTime time.Time
|
||||||
ActionLogFilePath string
|
ActionLogFilePath string
|
||||||
DeviceActionLogFilePath string
|
DeviceActionLogFilePath string
|
||||||
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -48,25 +49,58 @@ func GetConfig() *Config {
|
|||||||
|
|
||||||
startTimeStr := cfg.StartTime.Format("20060102150405")
|
startTimeStr := cfg.StartTime.Format("20060102150405")
|
||||||
cfg.ResultsDir = filepath.Join(ResultsDirName, startTimeStr)
|
cfg.ResultsDir = filepath.Join(ResultsDirName, startTimeStr)
|
||||||
cfg.ResultsPath = filepath.Join(cfg.RootDir, cfg.ResultsDir)
|
cfg.resultsPath = filepath.Join(cfg.RootDir, cfg.ResultsDir)
|
||||||
cfg.DownloadsPath = filepath.Join(cfg.RootDir, filepath.Join(DownloadsDirName, startTimeStr))
|
cfg.downloadsPath = filepath.Join(cfg.RootDir, filepath.Join(DownloadsDirName, startTimeStr))
|
||||||
cfg.ScreenShotsPath = filepath.Join(cfg.ResultsPath, ScreenshotsDirName)
|
cfg.screenShotsPath = filepath.Join(cfg.resultsPath, ScreenshotsDirName)
|
||||||
cfg.ActionLogFilePath = filepath.Join(cfg.ResultsDir, ActionLogDirName)
|
cfg.ActionLogFilePath = filepath.Join(cfg.ResultsDir, ActionLogDirName)
|
||||||
cfg.DeviceActionLogFilePath = "/sdcard/Android/data/io.appium.uiautomator2.server/files/hodor"
|
cfg.DeviceActionLogFilePath = "/sdcard/Android/data/io.appium.uiautomator2.server/files/hodor"
|
||||||
|
|
||||||
// create results directory
|
|
||||||
if err := builtin.EnsureFolderExists(cfg.ResultsPath); err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("create results directory failed")
|
|
||||||
}
|
|
||||||
if err := builtin.EnsureFolderExists(cfg.DownloadsPath); err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("create downloads directory failed")
|
|
||||||
}
|
|
||||||
if err := builtin.EnsureFolderExists(cfg.ScreenShotsPath); err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("create screenshots directory failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
globalConfig = cfg
|
globalConfig = cfg
|
||||||
})
|
})
|
||||||
|
|
||||||
return globalConfig
|
return globalConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResultsPath returns the results path and creates the directory if it doesn't exist
|
||||||
|
func (c *Config) ResultsPath() string {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
// Check if directory exists, create if it doesn't
|
||||||
|
if _, err := os.Stat(c.resultsPath); os.IsNotExist(err) {
|
||||||
|
if err := builtin.EnsureFolderExists(c.resultsPath); err != nil {
|
||||||
|
log.Error().Err(err).Str("path", c.resultsPath).Msg("failed to create results directory")
|
||||||
|
} else {
|
||||||
|
log.Info().Str("path", c.resultsPath).Msg("create folder")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.resultsPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// DownloadsPath returns the downloads path and creates the directory if it doesn't exist
|
||||||
|
func (c *Config) DownloadsPath() string {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
// Check if directory exists, create if it doesn't
|
||||||
|
if _, err := os.Stat(c.downloadsPath); os.IsNotExist(err) {
|
||||||
|
if err := builtin.EnsureFolderExists(c.downloadsPath); err != nil {
|
||||||
|
log.Error().Err(err).Str("path", c.downloadsPath).Msg("failed to create downloads directory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.downloadsPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScreenShotsPath returns the screenshots path and creates the directory if it doesn't exist
|
||||||
|
func (c *Config) ScreenShotsPath() string {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
// Check if directory exists, create if it doesn't
|
||||||
|
if _, err := os.Stat(c.screenShotsPath); os.IsNotExist(err) {
|
||||||
|
if err := builtin.EnsureFolderExists(c.screenShotsPath); err != nil {
|
||||||
|
log.Error().Err(err).Str("path", c.screenShotsPath).Msg("failed to create screenshots directory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.screenShotsPath
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v5.0.0-beta-2506101103
|
v5.0.0-beta-2506101206
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
"github.com/httprunner/httprunner/v5/internal/config"
|
"github.com/httprunner/httprunner/v5/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitLogger(logLevel string, logJSON bool) {
|
func InitLogger(logLevel string, logJSON bool, logFile bool) {
|
||||||
// Error Logging with Stacktrace
|
// Error Logging with Stacktrace
|
||||||
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
|
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
|
||||||
|
|
||||||
@@ -39,40 +39,52 @@ func InitLogger(logLevel string, logJSON bool) {
|
|||||||
TimeFormat: time.RFC3339Nano,
|
TimeFormat: time.RFC3339Nano,
|
||||||
NoColor: noColor,
|
NoColor: noColor,
|
||||||
}
|
}
|
||||||
|
if logFile {
|
||||||
msg = "log with colorized console and file output"
|
msg = "log with colorized console and file output"
|
||||||
|
} else {
|
||||||
|
msg = "log with colorized console output only"
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// default logger
|
// default logger
|
||||||
consoleWriter = os.Stderr
|
consoleWriter = os.Stderr
|
||||||
|
if logFile {
|
||||||
msg = "log with json console and file output"
|
msg = "log with json console and file output"
|
||||||
|
} else {
|
||||||
|
msg = "log with json console output only"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse console log level
|
// parse console log level
|
||||||
consoleLevel := parseLogLevel(logLevel)
|
consoleLevel := parseLogLevel(logLevel)
|
||||||
|
|
||||||
|
// If logFile is false, use console-only logger
|
||||||
|
if !logFile {
|
||||||
|
log.Logger = zerolog.New(consoleWriter).With().Timestamp().Logger().Level(consoleLevel)
|
||||||
|
log.Info().Msg(msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// file writer - write to results/taskID/hrp.log
|
// file writer - write to results/taskID/hrp.log
|
||||||
cfg := config.GetConfig()
|
cfg := config.GetConfig()
|
||||||
logFilePath := filepath.Join(cfg.ResultsPath, "hrp.log")
|
logFilePath := filepath.Join(cfg.ResultsPath(), "hrp.log")
|
||||||
|
|
||||||
// create or open log file
|
// create or open log file
|
||||||
logFile, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o666)
|
logFileWriter, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// if file creation failed, use console logger only
|
// if file creation failed, use console logger only
|
||||||
log.Logger = zerolog.New(consoleWriter).With().Timestamp().Logger().Level(consoleLevel)
|
log.Logger = zerolog.New(consoleWriter).With().Timestamp().Logger().Level(consoleLevel)
|
||||||
log.Error().Err(err).Str("logFilePath", logFilePath).Msg("create log file failed")
|
log.Error().Err(err).Str("logFilePath", logFilePath).Msg(msg)
|
||||||
} else {
|
} else {
|
||||||
// create a custom writer that applies different log levels
|
// create a custom writer that applies different log levels
|
||||||
multiWriter := &leveledMultiWriter{
|
multiWriter := &leveledMultiWriter{
|
||||||
consoleWriter: consoleWriter,
|
consoleWriter: consoleWriter,
|
||||||
consoleLevel: consoleLevel,
|
consoleLevel: consoleLevel,
|
||||||
fileWriter: logFile,
|
fileWriter: logFileWriter,
|
||||||
fileLevel: zerolog.DebugLevel,
|
fileLevel: zerolog.DebugLevel,
|
||||||
}
|
}
|
||||||
log.Logger = zerolog.New(multiWriter).With().Timestamp().Logger()
|
log.Logger = zerolog.New(multiWriter).With().Timestamp().Logger()
|
||||||
log.Info().Str("logFilePath", logFilePath).Msg("log file created successfully")
|
log.Info().Str("logFilePath", logFilePath).Msg(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info().Msg(msg)
|
|
||||||
log.Info().Str("console_log_level", strings.ToUpper(logLevel)).Str("file_log_level", "DEBUG").Msg("logger initialized with different levels")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseLogLevel converts string log level to zerolog.Level
|
// parseLogLevel converts string log level to zerolog.Level
|
||||||
|
|||||||
@@ -807,7 +807,7 @@ func (ad *ADBDriver) ScreenRecord(opts ...option.ActionOption) (videoPath string
|
|||||||
filePath = options.ScreenRecordPath
|
filePath = options.ScreenRecordPath
|
||||||
} else {
|
} else {
|
||||||
timestamp := time.Now().Format("20060102_150405") + fmt.Sprintf("_%03d", time.Now().UnixNano()/1e6%1000)
|
timestamp := time.Now().Format("20060102_150405") + fmt.Sprintf("_%03d", time.Now().UnixNano()/1e6%1000)
|
||||||
filePath = filepath.Join(config.GetConfig().ScreenShotsPath, fmt.Sprintf("%s.mp4", timestamp))
|
filePath = filepath.Join(config.GetConfig().ScreenShotsPath(), fmt.Sprintf("%s.mp4", timestamp))
|
||||||
}
|
}
|
||||||
|
|
||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ func (dExt *XTDriver) GetScreenResult(opts ...option.ActionOption) (screenResult
|
|||||||
fileName = builtin.GenNameWithTimestamp("%d_screenshot")
|
fileName = builtin.GenNameWithTimestamp("%d_screenshot")
|
||||||
}
|
}
|
||||||
imagePath := filepath.Join(
|
imagePath := filepath.Join(
|
||||||
config.GetConfig().ScreenShotsPath,
|
config.GetConfig().ScreenShotsPath(),
|
||||||
fmt.Sprintf("%s.%s", fileName, "jpeg"),
|
fmt.Sprintf("%s.%s", fileName, "jpeg"),
|
||||||
)
|
)
|
||||||
go func() {
|
go func() {
|
||||||
@@ -436,7 +436,7 @@ func MarkUIOperation(driver IDriver, actionType option.ActionName, actionCoordin
|
|||||||
// create screenshot save path
|
// create screenshot save path
|
||||||
timestamp := builtin.GenNameWithTimestamp("%d")
|
timestamp := builtin.GenNameWithTimestamp("%d")
|
||||||
imagePath := filepath.Join(
|
imagePath := filepath.Join(
|
||||||
config.GetConfig().ScreenShotsPath,
|
config.GetConfig().ScreenShotsPath(),
|
||||||
fmt.Sprintf("action_%s_pre_%s.png", timestamp, actionType),
|
fmt.Sprintf("action_%s_pre_%s.png", timestamp, actionType),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ func postHandler(driver IDriver, actionType option.ActionName, options *option.A
|
|||||||
// save compressed screenshot to file
|
// save compressed screenshot to file
|
||||||
timestamp := builtin.GenNameWithTimestamp("%d")
|
timestamp := builtin.GenNameWithTimestamp("%d")
|
||||||
imagePath := filepath.Join(
|
imagePath := filepath.Join(
|
||||||
config.GetConfig().ScreenShotsPath,
|
config.GetConfig().ScreenShotsPath(),
|
||||||
fmt.Sprintf("action_%s_post_%s.png", timestamp, actionType),
|
fmt.Sprintf("action_%s_post_%s.png", timestamp, actionType),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -305,7 +305,7 @@ var (
|
|||||||
func DownloadFileByUrl(fileUrl string) (filePath string, err error) {
|
func DownloadFileByUrl(fileUrl string) (filePath string, err error) {
|
||||||
hash := md5.Sum([]byte(fileUrl))
|
hash := md5.Sum([]byte(fileUrl))
|
||||||
fileName := fmt.Sprintf("%x", hash)
|
fileName := fmt.Sprintf("%x", hash)
|
||||||
filePath = filepath.Join(config.GetConfig().DownloadsPath, fileName)
|
filePath = filepath.Join(config.GetConfig().DownloadsPath(), fileName)
|
||||||
|
|
||||||
// get or create file lock
|
// get or create file lock
|
||||||
lockI, _ := fileLocks.LoadOrStore(filePath, &sync.Mutex{})
|
lockI, _ := fileLocks.LoadOrStore(filePath, &sync.Mutex{})
|
||||||
|
|||||||
@@ -910,7 +910,7 @@ func (wd *WDADriver) triggerWDALog(data map[string]interface{}) (rawResp []byte,
|
|||||||
func (wd *WDADriver) ScreenRecord(opts ...option.ActionOption) (videoPath string, err error) {
|
func (wd *WDADriver) ScreenRecord(opts ...option.ActionOption) (videoPath string, err error) {
|
||||||
log.Info().Msg("WDADriver.ScreenRecord")
|
log.Info().Msg("WDADriver.ScreenRecord")
|
||||||
timestamp := time.Now().Format("20060102_150405") + fmt.Sprintf("_%03d", time.Now().UnixNano()/1e6%1000)
|
timestamp := time.Now().Format("20060102_150405") + fmt.Sprintf("_%03d", time.Now().UnixNano()/1e6%1000)
|
||||||
fileName := filepath.Join(config.GetConfig().ScreenShotsPath, fmt.Sprintf("%s.mp4", timestamp))
|
fileName := filepath.Join(config.GetConfig().ScreenShotsPath(), fmt.Sprintf("%s.mp4", timestamp))
|
||||||
|
|
||||||
options := option.NewActionOptions(opts...)
|
options := option.NewActionOptions(opts...)
|
||||||
duration := time.Duration(options.Duration * float64(time.Second))
|
duration := time.Duration(options.Duration * float64(time.Second))
|
||||||
|
|||||||
Reference in New Issue
Block a user