fxcore

package module
v1.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 17, 2024 License: MIT Imports: 36 Imported by: 0

README

Fx Core Module

ci go report codecov Deps PkgGoDev

Fx core module.

Installation

go get github.com/ankorstore/yokai/fxcore

Features

The fxcore module provides the foundation of your application:

  • a bootstrapper
  • a dependency injection system
  • a dedicated core http server
  • ready to use config, health check, logger and tracer and metrics components
  • an extension system for Yokai built-in, contrib or your own modules

The core http server runs automatically on a dedicated port (default 8081), to serve:

  • the dashboard: UI to get an overview of your application
  • the metrics endpoint: to expose all collected metrics from your application
  • the health check endpoints: to expose all configured health check probes of your application
  • the debug endpoints: to expose various information about your config, modules, build, etc.

Whatever your type of application (httpserver, gRPC server, worker, etc.), all platform concerns are handled by this dedicated server:

  • to avoid to expose sensitive information (health checks, metrics, debug, etc) to your users
  • and most importantly to enable your application to focus on its logic

Documentation

Preloaded modules

This core module preloads:

Configuration

Configuration reference:

# ./configs/config.yaml
app:
  name: app
  env: dev
  version: 0.1.0
  debug: true
modules:
  log:
    level: info
    output: stdout
  trace:
    processor:
      type: stdout
  core:
    server:
      address: ":8081"                 # core http server listener address (default :8081)
      errors:              
        obfuscate: false               # to obfuscate error messages on the core http server responses
        stack: false                   # to add error stack trace to error response of the core http server
      dashboard:
        enabled: true                  # to enable the core dashboard
        overview:      
          app_env: true                # to display the app env on the dashboard overview
          app_debug: true              # to display the app debug on the dashboard overview
          app_version: true            # to display the app version on the dashboard overview
          log_level: true              # to display the log level on the dashboard overview
          log_output: true             # to display the log output on the dashboard overview
          trace_sampler: true          # to display the trace sampler on the dashboard overview
          trace_processor: true        # to display the trace processor on the dashboard overview
      log:
        headers:                       # to log incoming request headers on the core http server
          x-foo: foo                   # to log for example the header x-foo in the log field foo
          x-bar: bar              
        exclude:                       # to exclude specific routes from logging
          - /healthz
          - /livez
          - /readyz
          - /metrics
        level_from_response: true      # to use response status code for log level (ex: 500=error)
      trace:     
        enabled: true                  # to trace incoming request headers on the core http server
        exclude:                       # to exclude specific routes from tracing
          - /healthz     
          - /livez     
          - /readyz     
          - /metrics     
      metrics:     
        expose: true                   # to expose metrics route, disabled by default
        path: /metrics                 # metrics route path (default /metrics)
        collect:       
          enabled: true                # to collect core http server metrics, disabled by default
          namespace: foo               # core http server metrics namespace (empty by default)
        buckets: 0.1, 1, 10            # to override default request duration buckets
        normalize:
          request_path: true           # to normalize http request path, disabled by default
          response_status: true        # to normalize http response status code (2xx, 3xx, ...), disabled by default
      healthcheck:
        startup:
          expose: true                 # to expose health check startup route, disabled by default
          path: /healthz               # health check startup route path (default /healthz)
        readiness:            
          expose: true                 # to expose health check readiness route, disabled by default
          path: /readyz                # health check readiness route path (default /readyz)
        liveness:            
          expose: true                 # to expose health check liveness route, disabled by default
          path: /livez                 # health check liveness route path (default /livez)
      debug:
        config:
          expose: true                 # to expose debug config route
          path: /debug/config          # debug config route path (default /debug/config)
        pprof:
          expose: true                 # to expose debug pprof route
          path: /debug/pprof           # debug pprof route path (default /debug/pprof)
        routes:
          expose: true                 # to expose debug routes route
          path: /debug/routes          # debug routes route path (default /debug/routes)
        stats:
          expose: true                 # to expose debug stats route
          path: /debug/stats           # debug stats route path (default /debug/stats)
        build:
          expose: true                 # to expose debug build route
          path: /debug/build           # debug build route path (default /debug/build)
        modules:
          expose: true                 # to expose debug modules route
          path: /debug/modules/:name   # debug modules route path (default /debug/modules/:name)      

Notes:

  • the core http server requests logging will be based on the fxlog module configuration
  • the core http server requests tracing will be based on the fxtrace module configuration
  • if app.debug=true (or env var APP_DEBUG=true):
    • the dashboard will be automatically enabled
    • all the debug endpoints will be automatically exposed
    • error responses will not be obfuscated and stack trace will be added

Check the configuration files documentation for more details.

Bootstrap

The core module provides a bootstrapper:

  • to plug in all the Fx modules required by your application
  • to provide your own application modules and services
  • to start your application (real or test runtime)
Application

Create an application service, for example depending on a database connection:

package service

import (
	"gorm.io/gorm"
)

type ExampleService struct {
	db *gorm.DB
}

func NewExampleService(db *gorm.DB) *ExampleService {
	return &ExampleService{
		db: db,
	}
}

func (s *ExampleService) Ping() bool {
	return s.db.Ping() // simplification
}

Create your application Bootstrapper with your bootstrap options:

package bootstrap

import (
	"github.com/ankorstore/yokai/fxcore"
	"github.com/ankorstore/yokai/fxorm"
	"go.uber.org/fx"
	"gorm.io/gorm"
	"path/to/service"
)

var Bootstrapper = fxcore.NewBootstrapper().WithOptions(
	fxorm.FxOrmModule,                     // load the ORM module (provides *gorm.DB)
	fx.Provide(service.NewExampleService), // autowire your service (*gorm.DB auto injection)
	fxcore.AsCoreExtraInfo("foo", "bar"),  // register extra information to display on core dashboard
)

You can use the bootstrapper to start your application:

package main

import (
	"context"

	"github.com/ankorstore/yokai/fxcore"
	"path/to/bootstrap"
)

func main() {
	// run the application
	bootstrap.Bootstrapper.RunApp()

	// or you can also run the application with a specific root context
	bootstrap.Bootstrapper.WithContext(context.Background()).RunApp()

	// or you can also bootstrap and run it on your own
	app := bootstrap.Bootstrapper.BootstrapApp()
	app.Run()
}
Test application

You can reuse your Bootstrapper to run your application in test mode:

package main_test

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"go.uber.org/fx"
	"path/to/bootstrap"
	"path/to/service"
)

func TestExampleService(t *testing.T) {
	// *service.ExampleService instance to extract from your application
	var svc *service.ExampleService

	// run the app in test mode and populate the service
	bootstrap.Bootstrapper.RunTestApp(t, fx.Populate(&svc))

	// assertion example
	assert.True(t, svc.Ping())
}

You can also use BootstrapTestApp() to bootstrap in test mode and run it on your own:

testApp := bootstrap.Bootstrapper.BootstrapTestApp(t, ...)
testApp.RequireStart().RequireStop()

Note: bootstrapping your application in test mode will set APP_ENV=test, automatically loading your testing configuration.

Root dir

The core module provides the possibility to retrieve the root dir with RootDir(), useful for setting relative path to templates or configs.

package bootstrap

import (
	"github.com/ankorstore/yokai/fxcore"
)

var RootDir string

func init() {
	RootDir = fxcore.RootDir(0) // configure number of stack frames to ascend
}

Then you can then use the global RootDir variable in any packages:

package main

import (
	"fmt"

	"path/to/bootstrap"
)

func main() {
	fmt.Printf("root dir: %s", bootstrap.RootDir)
}

Or in any tests:

package main_test

import (
	"fmt"
	"testing"

	"path/to/bootstrap"
)

func TestSomething(t *testing.T) {
	t.Setenv("APP_CONFIG_PATH", fmt.Sprintf("%s/configs", bootstrap.RootDir))

	//...
}

Documentation

Index

Constants

View Source
const (
	ModuleName                      = "core"
	DefaultAddress                  = ":8081"
	DefaultMetricsPath              = "/metrics"
	DefaultHealthCheckStartupPath   = "/healthz"
	DefaultHealthCheckLivenessPath  = "/livez"
	DefaultHealthCheckReadinessPath = "/readyz"
	DefaultDebugConfigPath          = "/debug/config"
	DefaultDebugPProfPath           = "/debug/pprof"
	DefaultDebugBuildPath           = "/debug/build"
	DefaultDebugRoutesPath          = "/debug/routes"
	DefaultDebugStatsPath           = "/debug/stats"
	DefaultDebugModulesPath         = "/debug/modules"
	ThemeLight                      = "light"
	ThemeDark                       = "dark"
)

Variables

FxCoreModule is the Fx core module.

Functions

func AsCoreExtraInfo added in v1.2.0

func AsCoreExtraInfo(name string, value string) fx.Option

AsCoreExtraInfo registers extra information in the core.

func RootDir

func RootDir(skip int) string

RootDir returns the root dir, for a provided number of stack frames to ascend.

func Sanitize added in v1.4.0

func Sanitize(str string) string

Sanitize transforms a given string to not contain spaces or dashes, and to be in lower case.

func Split added in v1.4.0

func Split(str string) []string

Split trims and splits a provided string by comma.

Types

type Bootstrapper

type Bootstrapper struct {
	// contains filtered or unexported fields
}

Bootstrapper is the application bootstrapper, that can load a list fx.Option and run your application.

func NewBootstrapper

func NewBootstrapper() *Bootstrapper

NewBootstrapper returns a new Bootstrapper.

func (*Bootstrapper) BootstrapApp

func (b *Bootstrapper) BootstrapApp(options ...fx.Option) *fx.App

BootstrapApp boostrap the application, accepting optional bootstrap options.

func (*Bootstrapper) BootstrapTestApp

func (b *Bootstrapper) BootstrapTestApp(tb testing.TB, options ...fx.Option) *fxtest.App

BootstrapTestApp boostrap the application in test mode, accepting a testing context and optional bootstrap options.

func (*Bootstrapper) RunApp

func (b *Bootstrapper) RunApp(options ...fx.Option)

RunApp runs the application, accepting optional runtime options.

func (*Bootstrapper) RunTestApp

func (b *Bootstrapper) RunTestApp(tb testing.TB, options ...fx.Option)

RunTestApp runs the application in test mode, accepting a testing context and optional runtime options.

func (*Bootstrapper) WithContext

func (b *Bootstrapper) WithContext(ctx context.Context) *Bootstrapper

WithContext is used to pass a parent context.Context.

func (*Bootstrapper) WithOptions

func (b *Bootstrapper) WithOptions(options ...fx.Option) *Bootstrapper

WithOptions is used to pass a list of fx.Option.

type Core

type Core struct {
	// contains filtered or unexported fields
}

Core is the core component, holding the core config, health checker and http server.

func NewCore

func NewCore(config *config.Config, checker *healthcheck.Checker, httpServer *echo.Echo) *Core

NewCore returns a new Core.

func NewFxCore

func NewFxCore(p FxCoreParam) (*Core, error)

NewFxCore returns a new Core.

func (*Core) Checker

func (c *Core) Checker() *healthcheck.Checker

Checker returns the core health checker.

func (*Core) Config

func (c *Core) Config() *config.Config

Config returns the core config.

func (*Core) HttpServer

func (c *Core) HttpServer() *echo.Echo

HttpServer returns the core http server.

type DashboardRenderer

type DashboardRenderer struct {
	// contains filtered or unexported fields
}

DashboardRenderer is the core dashboard template renderer, based on template.Template.

func NewDashboardRenderer

func NewDashboardRenderer(fs embed.FS, tpl string) *DashboardRenderer

NewDashboardRenderer returns a new DashboardRenderer.

func (*DashboardRenderer) Render

func (r *DashboardRenderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error

Render renders the core dashboard template.

type FxCoreDashboardTheme

type FxCoreDashboardTheme struct {
	Theme string `form:"theme" json:"theme"`
}

FxCoreDashboardTheme is the theme for the core dashboard.

type FxCoreModuleInfo

type FxCoreModuleInfo struct {
	AppName        string
	AppEnv         string
	AppDebug       bool
	AppVersion     string
	LogLevel       string
	LogOutput      string
	TraceProcessor string
	TraceSampler   string
	ExtraInfos     map[string]string
}

FxCoreModuleInfo is a module info collector for the core.

func NewFxCoreModuleInfo

func NewFxCoreModuleInfo(p FxCoreModuleInfoParam) *FxCoreModuleInfo

NewFxCoreModuleInfo returns a new FxCoreModuleInfo.

func (*FxCoreModuleInfo) Data

func (i *FxCoreModuleInfo) Data() map[string]interface{}

Data return the data of the module info.

func (*FxCoreModuleInfo) Name

func (i *FxCoreModuleInfo) Name() string

Name return the name of the module info.

type FxCoreModuleInfoParam added in v1.2.0

type FxCoreModuleInfoParam struct {
	fx.In
	Config     *config.Config
	ExtraInfos []FxExtraInfo `group:"core-extra-infos"`
}

FxCoreModuleInfoParam allows injection of the required dependencies in NewFxCoreModuleInfo.

type FxCoreParam

type FxCoreParam struct {
	fx.In
	Context         context.Context
	LifeCycle       fx.Lifecycle
	Generator       uuid.UuidGenerator
	TracerProvider  oteltrace.TracerProvider
	Checker         *healthcheck.Checker
	Config          *config.Config
	Logger          *log.Logger
	Registry        *FxModuleInfoRegistry
	MetricsRegistry *prometheus.Registry
}

FxCoreParam allows injection of the required dependencies in NewFxCore.

type FxExtraInfo added in v1.2.0

type FxExtraInfo interface {
	Name() string
	Value() string
}

FxExtraInfo is the struct used by modules or apps to provide their extra info to the core.

func NewFxExtraInfo added in v1.2.0

func NewFxExtraInfo(name string, value string) FxExtraInfo

NewFxExtraInfo returns a new FxExtraInfo.

type FxModuleInfo

type FxModuleInfo interface {
	Name() string
	Data() map[string]any
}

FxModuleInfo is the interface to implement by modules to provide their info to the core.

type FxModuleInfoRegistry

type FxModuleInfoRegistry struct {
	// contains filtered or unexported fields
}

FxModuleInfoRegistry is the registry collecting info about registered modules.

func NewFxModuleInfoRegistry

func NewFxModuleInfoRegistry(p FxModuleInfoRegistryParam) *FxModuleInfoRegistry

NewFxModuleInfoRegistry returns a new FxModuleInfoRegistry.

func (*FxModuleInfoRegistry) All

All returns a map of all registered FxModuleInfo.

func (*FxModuleInfoRegistry) Find

func (r *FxModuleInfoRegistry) Find(name string) (FxModuleInfo, error)

Find returns a FxModuleInfo by name.

func (*FxModuleInfoRegistry) Names

func (r *FxModuleInfoRegistry) Names() []string

type FxModuleInfoRegistryParam

type FxModuleInfoRegistryParam struct {
	fx.In
	Infos []any `group:"core-module-infos"`
}

FxModuleInfoRegistryParam allows injection of the required dependencies in NewFxModuleInfoRegistry.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL