forked from gofiber/fiber
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathservices.go
More file actions
147 lines (124 loc) · 4.47 KB
/
services.go
File metadata and controls
147 lines (124 loc) · 4.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package fiber
import (
"context"
"errors"
"fmt"
"io"
"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
}
// 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 _, srv := range app.configured.Services {
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 _, srv := range app.state.Services() {
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
func (app *App) logServices(ctx context.Context, out io.Writer, colors Colors) {
if !app.hasConfiguredServices() {
return
}
fmt.Fprintf(out,
"%sINFO%s Services: \t%s%d%s\n",
colors.Green, colors.Reset, colors.Blue, app.state.ServicesLen(), colors.Reset)
for _, srv := range app.state.Services() {
var state string
var stateColor string
state, err := srv.State(ctx)
if err != nil {
state = "ERROR"
stateColor = colors.Red
} else {
stateColor = colors.Blue
state = strings.ToUpper(state)
}
fmt.Fprintf(out, "%sINFO%s 🥡 %s[ %s ] %s%s\n", colors.Green, colors.Reset, stateColor, strings.ToUpper(state), srv.String(), colors.Reset)
}
}