feat: adopt custom logger
This commit is contained in:
parent
7f97a23945
commit
1a5826149f
6 changed files with 92 additions and 21 deletions
|
@ -1,11 +1,11 @@
|
||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
logger "courgette/internal/logging"
|
||||||
runner "courgette/internal/runner"
|
runner "courgette/internal/runner"
|
||||||
workflow "courgette/internal/workflow"
|
workflow "courgette/internal/workflow"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExecuteWorkflow(configuration Configuration, workflowFile string) error {
|
func ExecuteWorkflow(configuration Configuration, workflowFile string) error {
|
||||||
|
@ -23,19 +23,19 @@ func ExecuteWorkflow(configuration Configuration, workflowFile string) error {
|
||||||
workflow, err := workflow.FromYamlFile(workflowFile)
|
workflow, err := workflow.FromYamlFile(workflowFile)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("%#v", err)
|
logger.Error(logger.Red("Failed to read workflow (%s)"), workflowFile)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
validationErrors := workflow.Validate()
|
validationErrors := workflow.Validate()
|
||||||
|
|
||||||
if len(validationErrors) > 0 {
|
if len(validationErrors) > 0 {
|
||||||
for _, err := range validationErrors {
|
for _, err := range validationErrors {
|
||||||
log.Printf("Validation error:: %#v", err)
|
logger.Error(logger.Red("Validation error: %s"), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.New("Jobs encountered errors.")
|
return errors.New("Workflow validation failed.")
|
||||||
}
|
}
|
||||||
|
|
||||||
taskResult := runnerInstance.RunWorkflow(*workflow)
|
taskResult := runnerInstance.RunWorkflow(*workflow)
|
||||||
|
|
||||||
if !taskResult.HasError() {
|
if !taskResult.HasError() {
|
||||||
|
@ -43,7 +43,11 @@ func ExecuteWorkflow(configuration Configuration, workflowFile string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, job := range taskResult.Context.Jobs {
|
for _, job := range taskResult.Context.Jobs {
|
||||||
log.Printf("Job %s: %s", job.Id, job.Status)
|
if job.Status == "success" {
|
||||||
|
logger.Info(logger.Green("Job %s: %s"), job.Id, job.Status)
|
||||||
|
} else if job.Status == "failed" {
|
||||||
|
logger.Error(logger.Red("Job %s: %s"), job.Id, job.Status)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("Task %s failed with at least 1 error.", taskResult.Id)
|
return fmt.Errorf("Task %s failed with at least 1 error.", taskResult.Id)
|
||||||
|
|
|
@ -1,27 +1,32 @@
|
||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
logger "courgette/internal/logging"
|
||||||
workflow "courgette/internal/workflow"
|
workflow "courgette/internal/workflow"
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ValidateWorkflow(configuration Configuration, workflowPath string) error {
|
func ValidateWorkflow(configuration Configuration, workflowPath string) error {
|
||||||
|
logger.Info("Validating workflow at \"%s\".", workflowPath)
|
||||||
|
|
||||||
workflow, err := workflow.FromYamlFile(workflowPath)
|
workflow, err := workflow.FromYamlFile(workflowPath)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("%#v", err)
|
logger.Error(logger.Red("Failed to read and parse workflow from \"%s\"."), workflowPath)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
validationErrors := workflow.Validate()
|
validationErrors := workflow.Validate()
|
||||||
|
|
||||||
if len(validationErrors) > 0 {
|
if len(validationErrors) > 0 {
|
||||||
for _, err := range validationErrors {
|
for _, err := range validationErrors {
|
||||||
log.Printf("Validation error:: %#v", err)
|
logger.Error(logger.Red("Validation error: %s"), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.New("Jobs encountered errors.")
|
return errors.New("Workflow validation failed.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.Info(logger.Green(logger.Bold("✅ Workflow \"%s\" is valid!")), workflowPath)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
17
internal/logging/colors.go
Normal file
17
internal/logging/colors.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// Color utilities to enhance text printed to the screen.
|
||||||
|
//
|
||||||
|
// See: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||||
|
|
||||||
|
package logging
|
||||||
|
|
||||||
|
func Bold(text string) string {
|
||||||
|
return "\033[1m" + text + "\033[0m"
|
||||||
|
}
|
||||||
|
|
||||||
|
func Green(text string) string {
|
||||||
|
return "\033[32m" + text + "\033[0m"
|
||||||
|
}
|
||||||
|
|
||||||
|
func Red(text string) string {
|
||||||
|
return "\033[31m" + text + "\033[0m"
|
||||||
|
}
|
44
internal/logging/logger.go
Normal file
44
internal/logging/logger.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// Logging module
|
||||||
|
//
|
||||||
|
// This module is a wrapper around the built-in `log` package
|
||||||
|
// and adds more control around if and how logging shows up
|
||||||
|
// in the code.
|
||||||
|
package logging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Logger struct {
|
||||||
|
Info log.Logger
|
||||||
|
Error log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
var Log Logger
|
||||||
|
|
||||||
|
// Configures the loggers and initializes each logging level's instance.
|
||||||
|
//
|
||||||
|
// This should be run once and before any logging is done.
|
||||||
|
func ConfigureLogger() {
|
||||||
|
Log = Logger{
|
||||||
|
Info: *log.New(os.Stdout, "[INFO] ", log.Ldate|log.Ltime),
|
||||||
|
Error: *log.New(os.Stderr, "[ERROR] ", log.Ldate|log.Ltime),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Info(message string, args ...any) {
|
||||||
|
if len(args) == 0 {
|
||||||
|
Log.Info.Print(message)
|
||||||
|
} else {
|
||||||
|
Log.Info.Printf(message, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Error(message string, args ...any) {
|
||||||
|
if len(args) == 0 {
|
||||||
|
Log.Error.Print(message)
|
||||||
|
} else {
|
||||||
|
Log.Error.Printf(message, args...)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
package runner
|
package runner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
logger "courgette/internal/logging"
|
||||||
workflow "courgette/internal/workflow"
|
workflow "courgette/internal/workflow"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ func (r *Runner) DeferTask(task func()) {
|
||||||
// Each task is executed within a go routine and the call will
|
// Each task is executed within a go routine and the call will
|
||||||
// wait until all the tasks are completed before returning.
|
// wait until all the tasks are completed before returning.
|
||||||
func (r *Runner) RunDeferredTasks() {
|
func (r *Runner) RunDeferredTasks() {
|
||||||
log.Printf("Running %d deferred tasks.", len(r.deferred))
|
logger.Info("Running %d deferred tasks.", len(r.deferred))
|
||||||
|
|
||||||
var tracker sync.WaitGroup
|
var tracker sync.WaitGroup
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ func (r *Runner) GetTask(taskId string) *Task {
|
||||||
// that the jobs will be executed in, run the jobs's steps and
|
// that the jobs will be executed in, run the jobs's steps and
|
||||||
// tear down the container once no longer useful.
|
// tear down the container once no longer useful.
|
||||||
func (r *Runner) RunWorkflow(workflow workflow.Workflow) Task {
|
func (r *Runner) RunWorkflow(workflow workflow.Workflow) Task {
|
||||||
log.Printf("Executing workflow: %s", workflow.SourcePath)
|
logger.Info("Executing workflow: %s", workflow.SourcePath)
|
||||||
task := r.GetTask(r.AddTask())
|
task := r.GetTask(r.AddTask())
|
||||||
|
|
||||||
for _, job := range workflow.Jobs {
|
for _, job := range workflow.Jobs {
|
||||||
|
@ -94,7 +94,7 @@ func (r *Runner) RunWorkflow(workflow workflow.Workflow) Task {
|
||||||
runnerImage := r.GetImageUriByLabel(job.RunsOn)
|
runnerImage := r.GetImageUriByLabel(job.RunsOn)
|
||||||
containerName := r.GetContainerName(jobContext.Id)
|
containerName := r.GetContainerName(jobContext.Id)
|
||||||
|
|
||||||
log.Printf("Using image %s (label: %s)", runnerImage, job.RunsOn)
|
logger.Info("Using image %s (label: %s)", runnerImage, job.RunsOn)
|
||||||
|
|
||||||
if pullError := r.Driver.Pull(runnerImage); pullError != nil {
|
if pullError := r.Driver.Pull(runnerImage); pullError != nil {
|
||||||
jobContext.SetStatus("failed").SetError(pullError)
|
jobContext.SetStatus("failed").SetError(pullError)
|
||||||
|
@ -139,13 +139,13 @@ func (r *Runner) RunJobInContainer(imageUri string, containerId string, job work
|
||||||
r.Driver.Start(imageUri, containerId)
|
r.Driver.Start(imageUri, containerId)
|
||||||
|
|
||||||
r.DeferTask(func() {
|
r.DeferTask(func() {
|
||||||
log.Printf("Started cleaning up %s", containerId)
|
logger.Info("Started cleaning up %s", containerId)
|
||||||
r.Driver.Stop(containerId)
|
r.Driver.Stop(containerId)
|
||||||
})
|
})
|
||||||
|
|
||||||
log.Printf("Started %s", containerId)
|
logger.Info("Started %s", containerId)
|
||||||
for _, step := range job.Steps {
|
for _, step := range job.Steps {
|
||||||
log.Printf("Run: %s", step.Run)
|
logger.Info("Run: %s", step.Run)
|
||||||
|
|
||||||
if err := r.RunCommandInContainer(containerId, step.Run); err != nil {
|
if err := r.RunCommandInContainer(containerId, step.Run); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
9
main.go
9
main.go
|
@ -3,14 +3,15 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
commands "courgette/internal/commands"
|
commands "courgette/internal/commands"
|
||||||
|
logger "courgette/internal/logging"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cli = &cobra.Command{
|
var cli = &cobra.Command{
|
||||||
Use: "runner",
|
Use: "runner",
|
||||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
logger.ConfigureLogger()
|
||||||
configPath, err := cmd.Flags().GetString("config")
|
configPath, err := cmd.Flags().GetString("config")
|
||||||
|
|
||||||
ctx := cmd.Context()
|
ctx := cmd.Context()
|
||||||
|
@ -19,7 +20,7 @@ var cli = &cobra.Command{
|
||||||
configuration, err := commands.NewConfigFromFile(configPath)
|
configuration, err := commands.NewConfigFromFile(configPath)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to parse configuration (%s)!", configPath)
|
logger.Error(logger.Red("Failed to parse configuration (%s)!"), configPath)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ var execute = &cobra.Command{
|
||||||
config := cmd.Context().Value("config").(*commands.Configuration)
|
config := cmd.Context().Value("config").(*commands.Configuration)
|
||||||
|
|
||||||
if err := commands.ExecuteWorkflow(*config, args[0]); err != nil {
|
if err := commands.ExecuteWorkflow(*config, args[0]); err != nil {
|
||||||
log.Printf("Failure: %s", err)
|
logger.Error(logger.Red("%s"), err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -52,7 +53,7 @@ var validate = &cobra.Command{
|
||||||
config := cmd.Context().Value("config").(*commands.Configuration)
|
config := cmd.Context().Value("config").(*commands.Configuration)
|
||||||
|
|
||||||
if err := commands.ValidateWorkflow(*config, args[0]); err != nil {
|
if err := commands.ValidateWorkflow(*config, args[0]); err != nil {
|
||||||
log.Printf("Failure: %s", err)
|
logger.Error(logger.Red("%s"), err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue