Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add export of service logs to dev run command #148

Merged
merged 8 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions modules/cli/cmd/devrunner/config/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const (
duskNum = "9"
dawnNum = "0"
BinariesDirName = "bin"
LogsDirName = "logs"
DataDirName = "data"
DefaultBaseConfigName = "base-config.toml"
DefaultCometbftGenesisFilename = "genesis.json"
Expand Down
4 changes: 4 additions & 0 deletions modules/cli/cmd/devrunner/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ func runInitialization(c *cobra.Command, _ []string) {
log.Info("Creating new instance in:", instanceDir)
cmd.CreateDirOrPanic(instanceDir)

// create a directory for all log files
logsDir := filepath.Join(instanceDir, config.LogsDirName)
cmd.CreateDirOrPanic(logsDir)

// create the local bin directory for downloaded binaries
localBinPath := filepath.Join(instanceDir, config.BinariesDirName)
log.Info("Binary files for locally running a sequencer placed in: ", localBinPath)
Expand Down
30 changes: 30 additions & 0 deletions modules/cli/cmd/devrunner/purge.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,41 @@ func purgeAllCmdHandler(c *cobra.Command, _ []string) {
log.Infof("Successfully deleted instance '%s'", instance)
}

// purgeLogsCmd represents the 'purge logs' command
var purgeLogsCmd = &cobra.Command{
Use: "logs",
Short: "Delete all logs for a given instance. Re-initializing is NOT required after using this command.",
Run: purgeLogsCmdHandler,
}

func purgeLogsCmdHandler(c *cobra.Command, _ []string) {
flagHandler := cmd.CreateCliFlagHandler(c, cmd.EnvPrefix)

instance := flagHandler.GetValue("instance")
config.IsInstanceNameValidOrPanic(instance)

homeDir := cmd.GetUserHomeDirOrPanic()
logDir := filepath.Join(homeDir, ".astria", instance, config.LogsDirName)

log.Infof("Deleting logs for instance '%s'", instance)

err := os.RemoveAll(logDir)
if err != nil {
fmt.Println("Error removing file:", err)
return
}
cmd.CreateDirOrPanic(logDir)

log.Infof("Successfully deleted logs for instance '%s'", instance)

}

func init() {
// top level command
devCmd.AddCommand(purgeCmd)

// subcommands
purgeCmd.AddCommand(purgeBinariesCmd)
purgeCmd.AddCommand(purgeAllCmd)
purgeCmd.AddCommand(purgeLogsCmd)
}
16 changes: 16 additions & 0 deletions modules/cli/cmd/devrunner/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func init() {
flagHandler.BindStringFlag("cometbft-path", "", "Provide an override path to a specific cometbft binary.")
flagHandler.BindStringFlag("composer-path", "", "Provide an override path to a specific composer binary.")
flagHandler.BindStringFlag("sequencer-path", "", "Provide an override path to a specific sequencer binary.")
flagHandler.BindBoolFlag("export-logs", false, "Export logs to files.")
}

func runCmdHandler(c *cobra.Command, _ []string) {
Expand All @@ -49,6 +50,11 @@ func runCmdHandler(c *cobra.Command, _ []string) {
instance := flagHandler.GetValue("instance")
config.IsInstanceNameValidOrPanic(instance)

exportLogs := flagHandler.GetValue("export-logs") == "true"
logsDir := filepath.Join(astriaDir, instance, config.LogsDirName)
currentTime := time.Now()
appStartTime := currentTime.Format("20060102-150405") // YYYYMMDD-HHMMSS

cmd.CreateUILog(filepath.Join(astriaDir, instance))

network := flagHandler.GetValue("network")
Expand Down Expand Up @@ -106,6 +112,8 @@ func runCmdHandler(c *cobra.Command, _ []string) {
Env: environment,
Args: nil,
ReadyCheck: &seqReadinessCheck,
LogPath: filepath.Join(logsDir, appStartTime+"-astria-sequencer.log"),
ExportLogs: exportLogs,
}
seqRunner = processrunner.NewProcessRunner(ctx, seqOpts)
case "composer":
Expand All @@ -116,6 +124,8 @@ func runCmdHandler(c *cobra.Command, _ []string) {
Env: environment,
Args: nil,
ReadyCheck: nil,
LogPath: filepath.Join(logsDir, appStartTime+"-astria-composer.log"),
ExportLogs: exportLogs,
}
compRunner = processrunner.NewProcessRunner(ctx, composerOpts)
case "conductor":
Expand All @@ -126,6 +136,8 @@ func runCmdHandler(c *cobra.Command, _ []string) {
Env: environment,
Args: nil,
ReadyCheck: nil,
LogPath: filepath.Join(logsDir, appStartTime+"-astria-conductor.log"),
ExportLogs: exportLogs,
}
condRunner = processrunner.NewProcessRunner(ctx, conductorOpts)
case "cometbft":
Expand All @@ -146,6 +158,8 @@ func runCmdHandler(c *cobra.Command, _ []string) {
Env: environment,
Args: []string{"node", "--home", cometDataPath, "--log_level", serviceLogLevel},
ReadyCheck: &cometReadinessCheck,
LogPath: filepath.Join(logsDir, appStartTime+"-cometbft.log"),
ExportLogs: exportLogs,
}
cometRunner = processrunner.NewProcessRunner(ctx, cometOpts)
default:
Expand All @@ -155,6 +169,8 @@ func runCmdHandler(c *cobra.Command, _ []string) {
Env: environment,
Args: nil, // TODO: implement generic args?
ReadyCheck: nil,
LogPath: filepath.Join(logsDir, appStartTime+"-"+service.Name+".log"),
ExportLogs: exportLogs,
}
genericRunner := processrunner.NewProcessRunner(ctx, genericOpts)
genericRunners = append(genericRunners, genericRunner)
Expand Down
61 changes: 61 additions & 0 deletions modules/cli/internal/processrunner/logHandler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package processrunner

import (
"os"
"regexp"

log "github.com/sirupsen/logrus"
)

type LogHandler struct {
logPath string
exportLogs bool
fileDescriptor *os.File
}

func NewLogHandler(logPath string, exportLogs bool) *LogHandler {
var fileDescriptor *os.File

// conditionally create the log file
if exportLogs {
// Open the log file
logFile, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatalf("error opening file: %v", err)
}
log.Info("New log file created successfully:", logPath)
fileDescriptor = logFile
} else {
fileDescriptor = nil
}

return &LogHandler{
logPath: logPath,
exportLogs: exportLogs,
fileDescriptor: fileDescriptor,
}
}

func (lh *LogHandler) Writeable() bool {
return lh.exportLogs
}

func (lh *LogHandler) Write(data string) error {
// Remove ANSI escape codes from data
ansiRegex := regexp.MustCompile(`\x1b\[[0-9;]*[mGKH]`)
cleanData := ansiRegex.ReplaceAllString(data, "")

_, err := lh.fileDescriptor.Write([]byte(cleanData))
if err != nil {
log.Fatalf("error writing to logfile %s: %v", lh.logPath, err)
return err
}
return nil
}

func (lh *LogHandler) Close() error {
if lh.fileDescriptor != nil {
return lh.fileDescriptor.Close()
}
return nil
}
23 changes: 23 additions & 0 deletions modules/cli/internal/processrunner/processrunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ type ProcessRunner interface {
GetOutputAndClearBuf() string
GetInfo() string
GetEnvironment() []string
CanWriteToLog() bool
WriteToLog(data string) error
}

// ProcessRunner is a struct that represents a process to be run.
Expand All @@ -41,6 +43,7 @@ type processRunner struct {
outputBuf *safebuffer.SafeBuffer

readyChecker *ReadyChecker
logHandler *LogHandler
}

type NewProcessRunnerOpts struct {
Expand All @@ -49,6 +52,8 @@ type NewProcessRunnerOpts struct {
Env []string
Args []string
ReadyCheck *ReadyChecker
LogPath string
ExportLogs bool
}

// NewProcessRunner creates a new ProcessRunner.
Expand All @@ -58,6 +63,7 @@ func NewProcessRunner(ctx context.Context, opts NewProcessRunnerOpts) ProcessRun
// using exec.CommandContext to allow for cancellation from caller
cmd := exec.CommandContext(ctx, opts.BinPath, opts.Args...)
cmd.Env = opts.Env
logHandler := NewLogHandler(opts.LogPath, opts.ExportLogs)
return &processRunner{
ctx: ctx,
cmd: cmd,
Expand All @@ -67,6 +73,7 @@ func NewProcessRunner(ctx context.Context, opts NewProcessRunnerOpts) ProcessRun
opts: opts,
env: opts.Env,
readyChecker: opts.ReadyCheck,
logHandler: logHandler,
}
}

Expand Down Expand Up @@ -188,6 +195,9 @@ func (pr *processRunner) Start(ctx context.Context, depStarted <-chan bool) erro

// Stop stops the process.
func (pr *processRunner) Stop() {
if err := pr.logHandler.Close(); err != nil {
log.WithError(err).Errorf("Error closing log file for process %s", pr.title)
}
// send SIGINT to the process
if err := pr.cmd.Process.Signal(syscall.SIGINT); err != nil {
log.WithError(err).Errorf("Error sending SIGINT for process %s", pr.title)
Expand Down Expand Up @@ -224,3 +234,16 @@ func (pr *processRunner) GetInfo() string {
func (pr *processRunner) GetEnvironment() []string {
return pr.env
}

func (pr *processRunner) CanWriteToLog() bool {
return pr.logHandler.Writeable()
}

func (pr *processRunner) WriteToLog(data string) error {
err := pr.logHandler.Write(data)
if err != nil {
return err
}

return nil
}
9 changes: 9 additions & 0 deletions modules/cli/internal/testutils/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,12 @@ func (m *MockProcessRunner) GetInfo() string {
args := m.Called()
return args.String(0)
}

func (m *MockProcessRunner) CanWriteToLog() bool {
return false
}

func (m *MockProcessRunner) WriteToLog(data string) error {
args := m.Called(data)
return args.Error(0)
}
8 changes: 8 additions & 0 deletions modules/cli/internal/ui/processpane.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ func (pp *ProcessPane) StartScan() {

// new, unprocessed data.
pp.tApp.QueueUpdateDraw(func() {
// write output data to logs if possible
if pp.pr.CanWriteToLog() {
err := pp.pr.WriteToLog(currentOutput)
if err != nil {
log.WithError(err).Error("Error writing to log")
}
}
// write output data to ui element
_, err := pp.ansiWriter.Write([]byte(currentOutput))
if err != nil {
log.WithError(err).Error("Error writing to textView")
Expand Down
Loading