adds configurable debugging of startup of processes and print out some info from the chrome viable() function for debugging purposes

This commit is contained in:
Thomas Bøgh Fangel
2019-06-13 15:59:06 +02:00
parent cfea2b8d9d
commit bbd0a893b1
7 changed files with 83 additions and 35 deletions

View File

@@ -25,6 +25,7 @@ const (
disableGoogleChromeEnvVar = "DISABLE_GOOGLE_CHROME" disableGoogleChromeEnvVar = "DISABLE_GOOGLE_CHROME"
disableUnoconvEnvVar = "DISABLE_UNOCONV" disableUnoconvEnvVar = "DISABLE_UNOCONV"
disableHealthcheckLoggingEnvVar = "DISABLE_HEALTHCHECK_LOGGING" disableHealthcheckLoggingEnvVar = "DISABLE_HEALTHCHECK_LOGGING"
debugProcessStartup = "DEBUG_PROCESS_STARTUP"
) )
func mustParseEnvVar() *api.Options { func mustParseEnvVar() *api.Options {
@@ -49,37 +50,31 @@ func mustParseEnvVar() *api.Options {
} }
opts.DefaultListenPort = v opts.DefaultListenPort = v
} }
if v, ok := os.LookupEnv(disableGoogleChromeEnvVar); ok { //checkBoolEnv is a convenience function for reading an env var with a bool value where `1` is true and `0` is false
if v != "1" && v != "0" { checkBoolEnv := func(name string) bool {
notify.ErrPrint(fmt.Errorf("%s: wrong value: want \"0\" or \"1\" got %v", disableGoogleChromeEnvVar, v)) if v, ok := os.LookupEnv(name); ok {
os.Exit(1) if v != "1" && v != "0" {
notify.ErrPrint(fmt.Errorf("%s: wrong value: want \"0\" or \"1\" got %v", name, v))
os.Exit(1)
}
return v == "1"
} }
opts.EnableChromeEndpoints = v != "1" return false
}
if v, ok := os.LookupEnv(disableUnoconvEnvVar); ok {
if v != "1" && v != "0" {
notify.ErrPrint(fmt.Errorf("%s: wrong value: want \"0\" or \"1\" got %v", disableUnoconvEnvVar, v))
os.Exit(1)
}
opts.EnableUnoconvEndpoints = v != "1"
}
if v, ok := os.LookupEnv(disableHealthcheckLoggingEnvVar); ok {
if v != "1" && v != "0" {
notify.ErrPrint(fmt.Errorf("%s: wrong value: want \"0\" or \"1\" got %v", disableHealthcheckLoggingEnvVar, v))
os.Exit(1)
}
opts.EnableHealthcheckLogging = v != "1"
} }
opts.EnableChromeEndpoints = !checkBoolEnv(disableGoogleChromeEnvVar)
opts.EnableUnoconvEndpoints = !checkBoolEnv(disableUnoconvEnvVar)
opts.EnableHealthcheckLogging = !checkBoolEnv(disableHealthcheckLoggingEnvVar)
opts.DebugProcessStartup = checkBoolEnv(debugProcessStartup)
return opts return opts
} }
func mustStartProcesses(opts *api.Options) []pm2.Process { func mustStartProcesses(opts *api.Options) []pm2.Process {
var processes []pm2.Process var processes []pm2.Process
if opts.EnableChromeEndpoints { if opts.EnableChromeEndpoints {
processes = append(processes, pm2.NewChrome()) processes = append(processes, pm2.NewChrome(opts.DebugProcessStartup))
} }
if opts.EnableUnoconvEndpoints { if opts.EnableUnoconvEndpoints {
processes = append(processes, pm2.NewUnoconv()) processes = append(processes, pm2.NewUnoconv(opts.DebugProcessStartup))
} }
for _, p := range processes { for _, p := range processes {
notify.Printf("starting %s with PM2...", p.Fullname()) notify.Printf("starting %s with PM2...", p.Fullname())

View File

@@ -1,8 +1,6 @@
package api package api
import ( import "github.com/labstack/echo/v4"
"github.com/labstack/echo/v4"
)
const pingEndpoint = "/ping" const pingEndpoint = "/ping"
@@ -14,6 +12,7 @@ type Options struct {
EnableChromeEndpoints bool EnableChromeEndpoints bool
EnableUnoconvEndpoints bool EnableUnoconvEndpoints bool
EnableHealthcheckLogging bool EnableHealthcheckLogging bool
DebugProcessStartup bool
} }
// DefaultOptions returns default options. // DefaultOptions returns default options.

View File

@@ -7,15 +7,19 @@ import (
"github.com/mafredri/cdp/devtool" "github.com/mafredri/cdp/devtool"
) )
const (
warmupTime = 10 * time.Second
)
type chrome struct { type chrome struct {
manager *processManager manager *processManager
} }
// NewChrome retruns a Google Chrome // NewChrome retruns a Google Chrome
// headless process. // headless process.
func NewChrome() Process { func NewChrome(debug bool) Process {
return &chrome{ return &chrome{
manager: &processManager{}, manager: &processManager{verbose: debug},
} }
} }
@@ -58,12 +62,20 @@ func (p *chrome) viable() bool {
// check if Google Chrome is correctly running. // check if Google Chrome is correctly running.
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
_, err := devtool.New("http://localhost:9222").Version(ctx) p.manager.notifyf(`%v: checking Chrome liveness via debug version endpoint
return err == nil 'http://localhost:9222/json/version'`, p.name())
v, err := devtool.New("http://localhost:9222").Version(ctx)
if err != nil {
p.manager.notifyf("%v: Chrome version endpoint returned error: %v", p.name(), err)
return false
}
p.manager.notifyf("%v: Chrome returned version info: %+v", p.name(), *v)
return true
} }
func (p *chrome) warmup() { func (p *chrome) warmup() {
time.Sleep(5 * time.Second) p.manager.notifyf("%v: allowing Chrome %v to startup", p.name(), warmupTime)
time.Sleep(warmupTime)
} }
// Compile-time checks to ensure type implements desired interfaces. // Compile-time checks to ensure type implements desired interfaces.

View File

@@ -7,13 +7,13 @@ import (
) )
func TestChromeStart(t *testing.T) { func TestChromeStart(t *testing.T) {
p := NewChrome() p := NewChrome(false)
err := p.Start() err := p.Start()
require.Nil(t, err) require.Nil(t, err)
} }
func TestChromeShutdown(t *testing.T) { func TestChromeShutdown(t *testing.T) {
p := NewChrome() p := NewChrome(false)
err := p.Shutdown() err := p.Shutdown()
require.Nil(t, err) require.Nil(t, err)
} }

View File

@@ -1,8 +1,14 @@
package pm2 package pm2
import ( import (
"bufio"
"fmt" "fmt"
"io"
"os/exec" "os/exec"
"strings"
"time"
"github.com/thecodingmachine/gotenberg/internal/pkg/notify"
) )
const ( const (
@@ -25,6 +31,7 @@ type Process interface {
type processManager struct { type processManager struct {
heuristicState int32 heuristicState int32
verbose bool
} }
func (m *processManager) start(p Process) error { func (m *processManager) start(p Process) error {
@@ -76,8 +83,43 @@ func (m *processManager) pm2(p Process, cmdName string) error {
"pm2", "pm2",
cmdArgs..., cmdArgs...,
) )
m.notifyf("executing command '%v'", strings.Join(cmd.Args, " "))
if m.verbose {
chromeStdErr, err := cmd.StderrPipe()
if err != nil {
return fmt.Errorf("failed getting Chrome stderr: %v", err)
}
chromeStdOut, err := cmd.StdoutPipe()
if err != nil {
return fmt.Errorf("failed getting Chrome stdout: %v", err)
}
readFromPipe := func(name string, reader io.ReadCloser) {
r := bufio.NewReader(reader)
defer reader.Close()
for {
line, _, err := r.ReadLine()
if err != nil {
if err != io.EOF {
m.notifyf("error reading from %v for process %v", name, p.name())
}
break
}
if len(line) != 0 {
m.notifyf("%v %v: %s", p.name(), name, string(line))
}
}
}
go readFromPipe("stdout", chromeStdOut)
go readFromPipe("stderr", chromeStdErr)
}
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
return fmt.Errorf("%s %s with PM2: %v", cmdName, p.Fullname(), err) return fmt.Errorf("%s %s with PM2: %v", cmdName, p.Fullname(), err)
} }
return nil return nil
} }
func (m *processManager) notifyf(format string, args ...interface{}) {
if m.verbose {
notify.Printf(fmt.Sprintf("%v: %s", time.Now().Format(time.RFC3339), format), args...)
}
}

View File

@@ -6,9 +6,9 @@ type unoconv struct {
// NewUnoconv retruns a unoconv listener // NewUnoconv retruns a unoconv listener
// process. // process.
func NewUnoconv() Process { func NewUnoconv(debug bool) Process {
return &unoconv{ return &unoconv{
manager: &processManager{}, manager: &processManager{verbose: debug},
} }
} }

View File

@@ -7,13 +7,13 @@ import (
) )
func TestUnoconvStart(t *testing.T) { func TestUnoconvStart(t *testing.T) {
p := NewUnoconv() p := NewUnoconv(false)
err := p.Start() err := p.Start()
require.Nil(t, err) require.Nil(t, err)
} }
func TestUnoconvShutdown(t *testing.T) { func TestUnoconvShutdown(t *testing.T) {
p := NewUnoconv() p := NewUnoconv(false)
err := p.Shutdown() err := p.Shutdown()
require.Nil(t, err) require.Nil(t, err)
} }