| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- package fiber
- import (
- "context"
- "errors"
- "fmt"
- "io"
- utilsstrings "github.com/gofiber/utils/v2/strings"
- )
- // Service is an interface that defines the methods for a service.
- type Service interface {
- // Start starts the service, returning an error if it fails.
- Start(ctx context.Context) error
- // String returns a string representation of the service.
- // It is used to print a human-readable name of the service in the startup message.
- String() string
- // State returns the current state of the service.
- State(ctx context.Context) (string, error)
- // Terminate terminates the service, returning an error if it fails.
- Terminate(ctx context.Context) error
- }
- // hasConfiguredServices Checks if there are any services for the current application.
- func (app *App) hasConfiguredServices() bool {
- return len(app.configured.Services) > 0
- }
- func (app *App) validateConfiguredServices() error {
- return validateServicesSlice(app.configured.Services)
- }
- func validateServicesSlice(services []Service) error {
- for idx, srv := range services {
- if srv == nil {
- return fmt.Errorf("fiber: service at index %d is nil", idx)
- }
- }
- return nil
- }
- // initServices If the app is configured to use services, this function registers
- // a post shutdown hook to shutdown them after the server is closed.
- // This function panics if there is an error starting the services.
- func (app *App) initServices() {
- if !app.hasConfiguredServices() {
- return
- }
- if err := app.startServices(app.servicesStartupCtx()); err != nil {
- panic(err)
- }
- }
- // servicesStartupCtx Returns the context for the services startup.
- // If the ServicesStartupContextProvider is not set, it returns a new background context.
- func (app *App) servicesStartupCtx() context.Context {
- if app.configured.ServicesStartupContextProvider != nil {
- return app.configured.ServicesStartupContextProvider()
- }
- return context.Background()
- }
- // servicesShutdownCtx Returns the context for the services shutdown.
- // If the ServicesShutdownContextProvider is not set, it returns a new background context.
- func (app *App) servicesShutdownCtx() context.Context {
- if app.configured.ServicesShutdownContextProvider != nil {
- return app.configured.ServicesShutdownContextProvider()
- }
- return context.Background()
- }
- // startServices Handles the start process of services for the current application.
- // Iterates over all configured services and tries to start them, returning an error if any error occurs.
- func (app *App) startServices(ctx context.Context) error {
- if !app.hasConfiguredServices() {
- return nil
- }
- var errs []error
- for idx, srv := range app.configured.Services {
- if srv == nil {
- return fmt.Errorf("fiber: service at index %d is nil", idx)
- }
- if err := ctx.Err(); err != nil {
- // Context is canceled, return an error the soonest possible, so that
- // the user can see the context cancellation error and act on it.
- return fmt.Errorf("context canceled while starting service %s: %w", srv.String(), err)
- }
- err := srv.Start(ctx)
- if err == nil {
- // mark the service as started
- app.state.setService(srv)
- continue
- }
- if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
- return fmt.Errorf("service %s start: %w", srv.String(), err)
- }
- errs = append(errs, fmt.Errorf("service %s start: %w", srv.String(), err))
- }
- return errors.Join(errs...)
- }
- // shutdownServices Handles the shutdown process of services for the current application.
- // Iterates over all the started services in reverse order and tries to terminate them,
- // returning an error if any error occurs.
- func (app *App) shutdownServices(ctx context.Context) error {
- if app.state.ServicesLen() == 0 {
- return nil
- }
- var errs []error
- for key, srv := range app.state.Services() {
- if srv == nil {
- return fmt.Errorf("fiber: service %q is nil", key)
- }
- if err := ctx.Err(); err != nil {
- // Context is canceled, do a best effort to terminate the services.
- errs = append(errs, fmt.Errorf("service %s terminate: %w", srv.String(), err))
- continue
- }
- err := srv.Terminate(ctx)
- if err != nil {
- // Best effort to terminate the services.
- errs = append(errs, fmt.Errorf("service %s terminate: %w", srv.String(), err))
- continue
- }
- // Remove the service from the State
- app.state.deleteService(srv)
- }
- return errors.Join(errs...)
- }
- // logServices logs information about services and returns an error
- // if any configured service is nil.
- func (app *App) logServices(ctx context.Context, out io.Writer, colors *Colors) error {
- if !app.hasConfiguredServices() {
- return nil
- }
- scheme := colors
- if scheme == nil {
- scheme = &DefaultColors
- }
- fmt.Fprintf(out,
- "%sINFO%s Services: \t%s%d%s\n",
- scheme.Green, scheme.Reset, scheme.Blue, app.state.ServicesLen(), scheme.Reset)
- for key, srv := range app.state.Services() {
- if srv == nil {
- return fmt.Errorf("fiber: service %q is nil", key)
- }
- var state string
- var stateColor string
- state, err := srv.State(ctx)
- if err != nil {
- state = errString
- stateColor = scheme.Red
- } else {
- stateColor = scheme.Blue
- }
- fmt.Fprintf(out, "%sINFO%s 🧩 %s[ %s ] %s%s\n", scheme.Green, scheme.Reset, stateColor, utilsstrings.ToUpper(state), srv.String(), scheme.Reset)
- }
- return nil
- }
|