Fx core module.
go get github.com/ankorstore/yokai/fxcore
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
This core module preloads:
- the fxconfig module
- the fxlog module
- the fxtrace module
- the fxmetrics module
- the fxgenerate module
- the fxhealthcheck module
Configuration reference:
# ./configs/config.yaml
app:
name: app
description: app description
env: dev
version: 0.1.0
debug: true
modules:
log:
level: info
output: stdout
trace:
processor:
type: stdout
core:
server:
expose: true # to expose the core http server, disabled by default
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)
tasks:
expose: true # to expose tasks route, disabled by default
path: /tasks/:name # tasks route path (default /tasks/:name)
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 varAPP_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.
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)
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()
}
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.
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))
//...
}