refactor: enhancement of logging, config and metrics
This commit is contained in:
@@ -18,9 +18,10 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
pkg "github.com/jkaninda/goma-gateway/internal"
|
pkg "github.com/jkaninda/goma-gateway/internal"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"log"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var CheckConfigCmd = &cobra.Command{
|
var CheckConfigCmd = &cobra.Command{
|
||||||
@@ -29,13 +30,15 @@ var CheckConfigCmd = &cobra.Command{
|
|||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
configFile, _ := cmd.Flags().GetString("config")
|
configFile, _ := cmd.Flags().GetString("config")
|
||||||
if configFile == "" {
|
if configFile == "" {
|
||||||
log.Fatalln("no config file specified")
|
fmt.Println("no config file specified")
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
err := pkg.CheckConfig(configFile)
|
err := pkg.CheckConfig(configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(" Error checking config file: %s\n", err)
|
fmt.Printf(" Error checking config file: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
log.Println("Goma Gateway configuration file checked successfully")
|
fmt.Println("Goma Gateway configuration file checked successfully")
|
||||||
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ limitations under the License.
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"log"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Cmd = &cobra.Command{
|
var Cmd = &cobra.Command{
|
||||||
@@ -28,8 +29,8 @@ var Cmd = &cobra.Command{
|
|||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
log.Fatalf("Config accepts no argument %q", args)
|
fmt.Printf("config accepts no argument %q\n", args)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,9 +16,10 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/jkaninda/goma-gateway/internal"
|
"github.com/jkaninda/goma-gateway/internal"
|
||||||
"github.com/jkaninda/goma-gateway/pkg/logger"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var InitConfigCmd = &cobra.Command{
|
var InitConfigCmd = &cobra.Command{
|
||||||
@@ -26,14 +27,34 @@ var InitConfigCmd = &cobra.Command{
|
|||||||
Short: "Initialize Goma Gateway configuration file",
|
Short: "Initialize Goma Gateway configuration file",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
pkg.InitConfig(cmd)
|
force, _ := cmd.Flags().GetBool("force")
|
||||||
|
configFile, _ := cmd.Flags().GetString("output")
|
||||||
|
if configFile == "" {
|
||||||
|
fmt.Println("Error: no config file specified")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
// Check if the config file exists
|
||||||
|
if _, err := os.Stat(configFile); !os.IsNotExist(err) {
|
||||||
|
if !force {
|
||||||
|
fmt.Printf("%s config file already exists, use -f to overwrite\n", configFile)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := pkg.InitConfig(configFile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Println("configuration file has been initialized successfully")
|
||||||
} else {
|
} else {
|
||||||
logger.Fatal(`"config" accepts no argument %q`, args)
|
fmt.Printf("config accepts no argument %q\n", args)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
InitConfigCmd.Flags().StringP("output", "o", "", "config file output")
|
InitConfigCmd.Flags().StringP("output", "o", "", "configuration file output")
|
||||||
|
InitConfigCmd.Flags().BoolP("force", "f", false, "Force overwrite configuration file")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,11 @@ limitations under the License.
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/jkaninda/goma-gateway/cmd/config"
|
"github.com/jkaninda/goma-gateway/cmd/config"
|
||||||
"github.com/jkaninda/goma-gateway/pkg/logger"
|
|
||||||
"github.com/jkaninda/goma-gateway/util"
|
"github.com/jkaninda/goma-gateway/util"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// rootCmd represents
|
// rootCmd represents
|
||||||
@@ -29,7 +30,7 @@ var rootCmd = &cobra.Command{
|
|||||||
Short: "Goma Gateway is a lightweight API Gateway Management",
|
Short: "Goma Gateway is a lightweight API Gateway Management",
|
||||||
Long: `.`,
|
Long: `.`,
|
||||||
Example: util.MainExample,
|
Example: util.MainExample,
|
||||||
Version: util.FullVersion(),
|
Version: util.Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||||
@@ -37,7 +38,8 @@ var rootCmd = &cobra.Command{
|
|||||||
func Execute() {
|
func Execute() {
|
||||||
err := rootCmd.Execute()
|
err := rootCmd.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal("Error executing root command %v", err)
|
fmt.Printf("Error executing root command %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/common-nighthawk/go-figure"
|
"github.com/common-nighthawk/go-figure"
|
||||||
"github.com/jkaninda/goma-gateway/internal"
|
"github.com/jkaninda/goma-gateway/internal"
|
||||||
"github.com/jkaninda/goma-gateway/pkg/logger"
|
|
||||||
"github.com/jkaninda/goma-gateway/util"
|
"github.com/jkaninda/goma-gateway/util"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ServerCmd = &cobra.Command{
|
var ServerCmd = &cobra.Command{
|
||||||
@@ -39,11 +39,13 @@ var ServerCmd = &cobra.Command{
|
|||||||
g := pkg.GatewayServer{}
|
g := pkg.GatewayServer{}
|
||||||
gs, err := g.Config(configFile)
|
gs, err := g.Config(configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal("Could not load configuration: %v", err)
|
fmt.Printf("Could not load configuration: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
gs.SetEnv()
|
gs.SetEnv()
|
||||||
if err := gs.Start(ctx); err != nil {
|
if err := gs.Start(ctx); err != nil {
|
||||||
logger.Fatal("Could not start server: %v", err)
|
fmt.Printf("Could not start server: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +58,7 @@ func init() {
|
|||||||
func intro() {
|
func intro() {
|
||||||
nameFigure := figure.NewFigure("Goma", "", true)
|
nameFigure := figure.NewFigure("Goma", "", true)
|
||||||
nameFigure.Print()
|
nameFigure.Print()
|
||||||
fmt.Printf("Version: %s\n", util.FullVersion())
|
fmt.Printf("Version: %s\n", util.Version)
|
||||||
fmt.Println("Copyright (c) 2024 Jonas Kaninda")
|
fmt.Println("Copyright (c) 2024 Jonas Kaninda")
|
||||||
fmt.Println("Starting Goma Gateway server...")
|
fmt.Println("Starting Goma Gateway server...")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/jkaninda/goma-gateway/util"
|
"github.com/jkaninda/goma-gateway/util"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@@ -32,5 +31,5 @@ var VersionCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func version() {
|
func version() {
|
||||||
fmt.Println("Version:\t", util.FullVersion())
|
util.FullVersion()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/jkaninda/goma-gateway/util"
|
"github.com/jkaninda/goma-gateway/util"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,21 +45,21 @@ func CheckConfig(fileName string) error {
|
|||||||
}
|
}
|
||||||
for index, route := range gateway.gateway.Routes {
|
for index, route := range gateway.gateway.Routes {
|
||||||
if len(route.Name) == 0 {
|
if len(route.Name) == 0 {
|
||||||
log.Printf("Warning: route name is empty, index: [%d]", index)
|
fmt.Printf("Warning: route name is empty, index: [%d]", index)
|
||||||
}
|
}
|
||||||
if route.Destination == "" && len(route.Backends) == 0 {
|
if route.Destination == "" && len(route.Backends) == 0 {
|
||||||
log.Printf("Error: no destination or backends specified for route: %s | index: [%d] \n", route.Name, index)
|
fmt.Printf("Error: no destination or backends specified for route: %s | index: [%d] \n", route.Name, index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check middleware
|
//Check middleware
|
||||||
for index, mid := range c.Middlewares {
|
for index, mid := range c.Middlewares {
|
||||||
if util.HasWhitespace(mid.Name) {
|
if util.HasWhitespace(mid.Name) {
|
||||||
log.Printf("Warning: Middleware contains whitespace: %s | index: [%d], please remove whitespace characters\n", mid.Name, index)
|
fmt.Printf("Warning: Middleware contains whitespace: %s | index: [%d], please remove whitespace characters\n", mid.Name, index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Routes count=%d Middlewares count=%d\n", len(gateway.gateway.Routes), len(gateway.middlewares))
|
fmt.Printf("Routes count=%d Middlewares count=%d\n", len(gateway.gateway.Routes), len(gateway.middlewares))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import (
|
|||||||
"github.com/jkaninda/goma-gateway/internal/middleware"
|
"github.com/jkaninda/goma-gateway/internal/middleware"
|
||||||
"github.com/jkaninda/goma-gateway/pkg/logger"
|
"github.com/jkaninda/goma-gateway/pkg/logger"
|
||||||
"github.com/jkaninda/goma-gateway/util"
|
"github.com/jkaninda/goma-gateway/util"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/oauth2/amazon"
|
"golang.org/x/oauth2/amazon"
|
||||||
"golang.org/x/oauth2/facebook"
|
"golang.org/x/oauth2/facebook"
|
||||||
@@ -83,7 +82,11 @@ func (GatewayServer) Config(configFile string) (*GatewayServer, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
initConfig(ConfigFile)
|
err := initConfig(ConfigFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
logger.Info("Generating new configuration file...done")
|
||||||
logger.Info("Server configuration file is available at %s", ConfigFile)
|
logger.Info("Server configuration file is available at %s", ConfigFile)
|
||||||
util.SetEnv("GOMA_CONFIG_FILE", ConfigFile)
|
util.SetEnv("GOMA_CONFIG_FILE", ConfigFile)
|
||||||
buf, err := os.ReadFile(ConfigFile)
|
buf, err := os.ReadFile(ConfigFile)
|
||||||
@@ -115,18 +118,13 @@ func GetConfigPaths() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// InitConfig initializes configs
|
// InitConfig initializes configs
|
||||||
func InitConfig(cmd *cobra.Command) {
|
func InitConfig(configFile string) error {
|
||||||
configFile, _ := cmd.Flags().GetString("output")
|
return initConfig(configFile)
|
||||||
if configFile == "" {
|
|
||||||
configFile = GetConfigPaths()
|
|
||||||
}
|
|
||||||
initConfig(configFile)
|
|
||||||
return
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initConfig initializes configs
|
// initConfig initializes configs
|
||||||
func initConfig(configFile string) {
|
func initConfig(configFile string) error {
|
||||||
if configFile == "" {
|
if configFile == "" {
|
||||||
configFile = GetConfigPaths()
|
configFile = GetConfigPaths()
|
||||||
}
|
}
|
||||||
@@ -292,13 +290,13 @@ func initConfig(configFile string) {
|
|||||||
}
|
}
|
||||||
yamlData, err := yaml.Marshal(&conf)
|
yamlData, err := yaml.Marshal(&conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal("Error serializing configuration %v", err.Error())
|
return fmt.Errorf("serializing configuration %v\n", err.Error())
|
||||||
}
|
}
|
||||||
err = os.WriteFile(configFile, yamlData, 0644)
|
err = os.WriteFile(configFile, yamlData, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal("Unable to write config file %s", err)
|
return fmt.Errorf("unable to write config file %s\n", err)
|
||||||
}
|
}
|
||||||
logger.Info("Configuration file has been initialized successfully")
|
return nil
|
||||||
}
|
}
|
||||||
func (Gateway) Setup(conf string) *Gateway {
|
func (Gateway) Setup(conf string) *Gateway {
|
||||||
if util.FileExists(conf) {
|
if util.FileExists(conf) {
|
||||||
|
|||||||
@@ -27,7 +27,12 @@ func printRoute(routes []Route) {
|
|||||||
t := table.NewWriter()
|
t := table.NewWriter()
|
||||||
t.AppendHeader(table.Row{"Name", "Route", "Rewrite", "Destination"})
|
t.AppendHeader(table.Row{"Name", "Route", "Rewrite", "Destination"})
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
t.AppendRow(table.Row{route.Name, route.Path, route.Rewrite, route.Destination})
|
if len(route.Backends) > 0 {
|
||||||
|
t.AppendRow(table.Row{route.Name, route.Path, route.Rewrite, fmt.Sprintf("backends: [%d]", len(route.Backends))})
|
||||||
|
|
||||||
|
} else {
|
||||||
|
t.AppendRow(table.Row{route.Name, route.Path, route.Rewrite, route.Destination})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fmt.Println(t.Render())
|
fmt.Println(t.Render())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ var totalRequests = prometheus.NewCounterVec(
|
|||||||
Name: "http_requests_total",
|
Name: "http_requests_total",
|
||||||
Help: "Number of get requests.",
|
Help: "Number of get requests.",
|
||||||
},
|
},
|
||||||
[]string{"path"},
|
[]string{"name", "path"},
|
||||||
)
|
)
|
||||||
|
|
||||||
var responseStatus = prometheus.NewCounterVec(
|
var responseStatus = prometheus.NewCounterVec(
|
||||||
@@ -49,16 +49,19 @@ var responseStatus = prometheus.NewCounterVec(
|
|||||||
var httpDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
var httpDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||||
Name: "http_response_time_seconds",
|
Name: "http_response_time_seconds",
|
||||||
Help: "Duration of HTTP requests.",
|
Help: "Duration of HTTP requests.",
|
||||||
}, []string{"path"})
|
}, []string{"name", "path"})
|
||||||
|
|
||||||
func prometheusMiddleware(next http.Handler) http.Handler {
|
func (pr PrometheusRoute) prometheusMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
route := mux.CurrentRoute(r)
|
path := pr.path
|
||||||
path, _ := route.GetPathTemplate()
|
if len(path) == 0 {
|
||||||
timer := prometheus.NewTimer(httpDuration.WithLabelValues(path))
|
route := mux.CurrentRoute(r)
|
||||||
|
path, _ = route.GetPathTemplate()
|
||||||
|
}
|
||||||
|
timer := prometheus.NewTimer(httpDuration.WithLabelValues(pr.name, path))
|
||||||
|
|
||||||
responseStatus.WithLabelValues(strconv.Itoa(http.StatusOK)).Inc()
|
responseStatus.WithLabelValues(strconv.Itoa(http.StatusOK)).Inc()
|
||||||
totalRequests.WithLabelValues(path).Inc()
|
totalRequests.WithLabelValues(pr.name, path).Inc()
|
||||||
|
|
||||||
timer.ObserveDuration()
|
timer.ObserveDuration()
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
|
|||||||
@@ -43,9 +43,7 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router {
|
|||||||
if gateway.EnableMetrics {
|
if gateway.EnableMetrics {
|
||||||
// Prometheus endpoint
|
// Prometheus endpoint
|
||||||
r.Path("/metrics").Handler(promhttp.Handler())
|
r.Path("/metrics").Handler(promhttp.Handler())
|
||||||
r.Use(prometheusMiddleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routes health check
|
// Routes health check
|
||||||
if !gateway.DisableHealthCheckStatus {
|
if !gateway.DisableHealthCheckStatus {
|
||||||
r.HandleFunc("/healthz", heath.HealthCheckHandler).Methods("GET")
|
r.HandleFunc("/healthz", heath.HealthCheckHandler).Methods("GET")
|
||||||
@@ -238,7 +236,14 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router {
|
|||||||
} else {
|
} else {
|
||||||
router.PathPrefix("").Handler(proxyRoute.ProxyHandler())
|
router.PathPrefix("").Handler(proxyRoute.ProxyHandler())
|
||||||
}
|
}
|
||||||
|
if gateway.EnableMetrics {
|
||||||
|
pr := PrometheusRoute{
|
||||||
|
name: route.Name,
|
||||||
|
path: route.Path,
|
||||||
|
}
|
||||||
|
// Prometheus endpoint
|
||||||
|
router.Use(pr.prometheusMiddleware)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.Error("Error, path is empty in route %s", route.Name)
|
logger.Error("Error, path is empty in route %s", route.Name)
|
||||||
logger.Error("Route path ignored: %s", route.Path)
|
logger.Error("Route path ignored: %s", route.Path)
|
||||||
|
|||||||
@@ -10,26 +10,20 @@ You may get a copy of the License at
|
|||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*/
|
*/
|
||||||
import (
|
import (
|
||||||
"os"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Version string
|
|
||||||
|
|
||||||
const ConfigVersion = "1.0"
|
const ConfigVersion = "1.0"
|
||||||
|
|
||||||
func VERSION(def string) string {
|
var Version = "development"
|
||||||
build := os.Getenv("VERSION")
|
var buildTime string
|
||||||
if build == "" {
|
var gitCommit string
|
||||||
return def
|
|
||||||
}
|
func FullVersion() {
|
||||||
return build
|
fmt.Printf("Goma Gateway version: %s\n", Version)
|
||||||
}
|
fmt.Printf("Configuration version: %s\n", ConfigVersion)
|
||||||
func FullVersion() string {
|
fmt.Printf("Build time: %s\n", buildTime)
|
||||||
ver := Version
|
fmt.Printf("Git commit: %s\n", gitCommit)
|
||||||
if b := VERSION(""); b != "" {
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
return ver
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MainExample = "Initialize config: config init --output config.yml\n" +
|
const MainExample = "Initialize config: config init --output config.yml\n" +
|
||||||
|
|||||||
Reference in New Issue
Block a user