mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-14 19:32:15 +01:00
wip refactoring: better logging and error systems
This commit is contained in:
122
internal/pkg/config/config.go
Normal file
122
internal/pkg/config/config.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultWaitTimeoutEnvVar = "DEFAULT_WAIT_TIMEOUT"
|
||||
defaultListenPortEnvVar = "DEFAULT_LISTEN_PORT"
|
||||
disableGoogleChromeEnvVar = "DISABLE_GOOGLE_CHROME"
|
||||
disableUnoconvEnvVar = "DISABLE_UNOCONV"
|
||||
logLevelEnvVar = "LOG_LEVEL"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
defaultWaitTimeout float64
|
||||
defaultListenPort string
|
||||
enableChromeEndpoints bool
|
||||
enableUnoconvEndpoints bool
|
||||
logLevel log.Level
|
||||
}
|
||||
|
||||
func defaultConfig() *Config {
|
||||
return &Config{
|
||||
defaultWaitTimeout: 10,
|
||||
defaultListenPort: "3000",
|
||||
enableChromeEndpoints: true,
|
||||
enableUnoconvEndpoints: true,
|
||||
logLevel: log.InfoLevel,
|
||||
}
|
||||
}
|
||||
|
||||
func FromEnv() (*Config, error) {
|
||||
c := defaultConfig()
|
||||
defaultWaitTimeout, err := defaultWaitTimeoutFromEnv(defaultWaitTimeoutEnvVar, c.DefaultWaitTimeout())
|
||||
c.defaultWaitTimeout = defaultWaitTimeout
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
defaultListenPort, err := defaultListenPortFromEnv(defaultListenPortEnvVar, c.DefaultListenPort())
|
||||
c.defaultListenPort = defaultListenPort
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
disableChromeEndpoints, err := boolFromEnv(disableGoogleChromeEnvVar, c.EnableChromeEndpoints())
|
||||
c.enableChromeEndpoints = !disableChromeEndpoints
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
disableUnoconvEndpoints, err := boolFromEnv(disableUnoconvEnvVar, c.EnableUnoconvEndpoints())
|
||||
c.enableUnoconvEndpoints = !disableUnoconvEndpoints
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
logLevel, err := logLevelFromEnv(logLevelEnvVar, c.LogLevel())
|
||||
c.logLevel = logLevel
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Config) DefaultWaitTimeout() float64 { return c.defaultWaitTimeout }
|
||||
func (c *Config) DefaultListenPort() string { return c.defaultListenPort }
|
||||
func (c *Config) EnableChromeEndpoints() bool { return c.enableChromeEndpoints }
|
||||
func (c *Config) EnableUnoconvEndpoints() bool { return c.enableUnoconvEndpoints }
|
||||
func (c *Config) LogLevel() log.Level { return c.logLevel }
|
||||
|
||||
func defaultWaitTimeoutFromEnv(envVar string, defaultValue float64) (float64, error) {
|
||||
if v, ok := os.LookupEnv(envVar); ok {
|
||||
waitTimeout, err := strconv.ParseFloat(v, 64)
|
||||
if err != nil {
|
||||
return defaultValue, fmt.Errorf("%s: wrong value: want float got %v", envVar, err)
|
||||
}
|
||||
return waitTimeout, nil
|
||||
}
|
||||
return defaultValue, nil
|
||||
}
|
||||
|
||||
func defaultListenPortFromEnv(envVar string, defaultValue string) (string, error) {
|
||||
if v, ok := os.LookupEnv(envVar); ok {
|
||||
portAsUint, err := strconv.ParseUint(v, 10, 64)
|
||||
if err != nil {
|
||||
return defaultValue, fmt.Errorf("%s: wrong value: want uint got %v", envVar, err)
|
||||
}
|
||||
if portAsUint > 65535 {
|
||||
return defaultValue, fmt.Errorf("%s: wrong value: want uint < 65535 got %d", envVar, portAsUint)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
return defaultValue, nil
|
||||
}
|
||||
|
||||
func boolFromEnv(envVar string, defaultValue bool) (bool, error) {
|
||||
if v, ok := os.LookupEnv(envVar); ok {
|
||||
if v != "1" && v != "0" {
|
||||
return defaultValue, fmt.Errorf("%s: wrong value: want \"0\" or \"1\" got %s", envVar, v)
|
||||
}
|
||||
return v == "1", nil
|
||||
}
|
||||
return defaultValue, nil
|
||||
}
|
||||
|
||||
func logLevelFromEnv(envVar string, defaultValue log.Level) (log.Level, error) {
|
||||
if v, ok := os.LookupEnv(envVar); ok {
|
||||
switch v {
|
||||
case "DEBUG":
|
||||
return log.DebugLevel, nil
|
||||
case "INFO":
|
||||
return log.InfoLevel, nil
|
||||
case "ERROR":
|
||||
return log.ErrorLevel, nil
|
||||
default:
|
||||
return defaultValue, fmt.Errorf("%s: wrong value: want \"DEBUG\",\"INFO\" or \"ERROR\" got %s", envVar, v)
|
||||
}
|
||||
}
|
||||
return defaultValue, nil
|
||||
}
|
||||
1
internal/pkg/config/doc.go
Normal file
1
internal/pkg/config/doc.go
Normal file
@@ -0,0 +1 @@
|
||||
package config
|
||||
1
internal/pkg/logger/doc.go
Normal file
1
internal/pkg/logger/doc.go
Normal file
@@ -0,0 +1 @@
|
||||
package logger
|
||||
33
internal/pkg/logger/logger.go
Normal file
33
internal/pkg/logger/logger.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Logger enforces specific log message formats.
|
||||
type Logger struct {
|
||||
*logrus.Entry
|
||||
}
|
||||
|
||||
// New initializes the logger.
|
||||
func New(level logrus.Level, trace string) *Logger {
|
||||
l := logrus.New()
|
||||
l.SetLevel(level)
|
||||
// TODO no formatter if TTY.
|
||||
l.SetFormatter(&logrus.JSONFormatter{})
|
||||
return &Logger{
|
||||
l.WithField("trace", trace),
|
||||
}
|
||||
}
|
||||
|
||||
// DebugfOp logs a debug message for given
|
||||
// logical operation.
|
||||
func (l *Logger) DebugfOp(op string, format string, args ...interface{}) {
|
||||
l.WithField("op", op).Debugf(format, args...)
|
||||
}
|
||||
|
||||
// ErrorOp logs an error message for given
|
||||
// logical operation.
|
||||
func (l *Logger) ErrorOp(op string, err error) {
|
||||
l.WithField("op", op).Error(err.Error())
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/mafredri/cdp/devtool"
|
||||
log "github.com/thecodingmachine/gotenberg/internal/pkg/logger"
|
||||
"github.com/thecodingmachine/gotenberg/internal/pkg/logger"
|
||||
)
|
||||
|
||||
const warmupTime = 10 * time.Second
|
||||
@@ -16,7 +16,7 @@ type chrome struct {
|
||||
|
||||
// NewChrome returns a Google Chrome
|
||||
// headless process.
|
||||
func NewChrome(logger *log.StandardLogger) Process {
|
||||
func NewChrome(logger *logger.Logger) Process {
|
||||
return &chrome{
|
||||
manager: &processManager{logger: logger},
|
||||
}
|
||||
@@ -61,7 +61,10 @@ func (p *chrome) viable() bool {
|
||||
// check if Google Chrome is correctly running.
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
p.manager.logger.Debugf("%s: checking liveness via debug version endpoint http://localhost:9222/json/version", p.Fullname())
|
||||
p.manager.logger.Debugf(
|
||||
"%s: checking liveness via debug version endpoint http://localhost:9222/json/version",
|
||||
p.Fullname(),
|
||||
)
|
||||
v, err := devtool.New("http://localhost:9222").Version(ctx)
|
||||
if err != nil {
|
||||
p.manager.logger.Debugf("%s: debug version endpoint returned error: %v", p.Fullname(), err)
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
log "github.com/thecodingmachine/gotenberg/internal/pkg/logger"
|
||||
"github.com/thecodingmachine/gotenberg/internal/pkg/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -30,7 +30,7 @@ type Process interface {
|
||||
|
||||
type processManager struct {
|
||||
heuristicState int32
|
||||
logger *log.StandardLogger
|
||||
logger *logger.Logger
|
||||
}
|
||||
|
||||
func (m *processManager) start(p Process) error {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package pm2
|
||||
|
||||
import (
|
||||
log "github.com/thecodingmachine/gotenberg/internal/pkg/logger"
|
||||
"github.com/thecodingmachine/gotenberg/internal/pkg/logger"
|
||||
)
|
||||
|
||||
type unoconv struct {
|
||||
@@ -10,7 +10,7 @@ type unoconv struct {
|
||||
|
||||
// NewUnoconv returns a unoconv listener
|
||||
// process.
|
||||
func NewUnoconv(logger *log.StandardLogger) Process {
|
||||
func NewUnoconv(logger *logger.Logger) Process {
|
||||
return &unoconv{
|
||||
manager: &processManager{logger: logger},
|
||||
}
|
||||
|
||||
3
internal/pkg/random/doc.go
Normal file
3
internal/pkg/random/doc.go
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package random helps generating
|
||||
// a random string.
|
||||
package random
|
||||
10
internal/pkg/random/random.go
Normal file
10
internal/pkg/random/random.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package random
|
||||
|
||||
import (
|
||||
"github.com/labstack/gommon/random"
|
||||
)
|
||||
|
||||
// Get returns a random string.
|
||||
func Get() string {
|
||||
return random.String(32)
|
||||
}
|
||||
5
internal/pkg/standarderror/doc.go
Normal file
5
internal/pkg/standarderror/doc.go
Normal file
@@ -0,0 +1,5 @@
|
||||
// Package standarderror helps standardizing
|
||||
// the errors in the application.
|
||||
//
|
||||
// Credits: https://middlemost.com/failure-is-your-domain/
|
||||
package standarderror
|
||||
85
internal/pkg/standarderror/standarderror.go
Normal file
85
internal/pkg/standarderror/standarderror.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package standarderror
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
// Internal is a code
|
||||
// for internal errors.
|
||||
Internal = "internal"
|
||||
// Invalid is a code
|
||||
// for validation errors.
|
||||
Invalid = "invalid"
|
||||
// Timeout is a code
|
||||
// for timeout errors.
|
||||
Timeout = "timeout"
|
||||
)
|
||||
|
||||
// Error defines a standard application
|
||||
// error.
|
||||
type Error struct {
|
||||
// Code is a machine-readable
|
||||
// error code.
|
||||
Code string
|
||||
// Message is a human-readable
|
||||
// message.
|
||||
Message string
|
||||
// Op is a logical operation.
|
||||
Op string
|
||||
// Err is a nested error.
|
||||
Err error
|
||||
}
|
||||
|
||||
// Error returns the string representation of the error message.
|
||||
func (err *Error) Error() string {
|
||||
var buf bytes.Buffer
|
||||
// print the current operation in our stack, if any.
|
||||
if err.Op != "" {
|
||||
fmt.Fprintf(&buf, "%s: ", err.Op)
|
||||
}
|
||||
// if wrapping an error, print its Error() message.
|
||||
// Otherwise print the error code & message.
|
||||
if err.Err != nil {
|
||||
buf.WriteString(err.Err.Error())
|
||||
} else {
|
||||
if err.Code != "" {
|
||||
fmt.Fprintf(&buf, "<%s> ", err.Code)
|
||||
}
|
||||
buf.WriteString(err.Message)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Code returns the code of the root error, if available.
|
||||
// Otherwise returns Internal.
|
||||
func Code(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
e, ok := err.(*Error)
|
||||
if ok && e.Code != "" {
|
||||
return e.Code
|
||||
}
|
||||
if ok && e.Err != nil {
|
||||
return Code(e.Err)
|
||||
}
|
||||
return Internal
|
||||
}
|
||||
|
||||
// Message returns the human-readable message of the error, if available.
|
||||
// Otherwise returns a generic error message.
|
||||
func Message(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
e, ok := err.(*Error)
|
||||
if ok && e.Message != "" {
|
||||
return e.Message
|
||||
}
|
||||
if ok && e.Err != nil {
|
||||
return Message(e.Err)
|
||||
}
|
||||
return "An internal error has occurred. Please contact technical support."
|
||||
}
|
||||
Reference in New Issue
Block a user