@@ -3,6 +3,7 @@ package gofr
3
3
import (
4
4
"context"
5
5
"errors"
6
+ "fmt"
6
7
"net"
7
8
"reflect"
8
9
"strconv"
@@ -42,10 +43,15 @@ type grpcServer struct {
42
43
// This function accepts a variadic list of gRPC server options (grpc.ServerOption) and appends them
43
44
// to the server's configuration. It allows fine-tuning of the gRPC server's behavior during its initialization.
44
45
func (a * App ) AddGRPCServerOptions (grpcOpts ... grpc.ServerOption ) {
46
+ if len (grpcOpts ) == 0 {
47
+ a .container .Logger .Debug ("no gRPC server options provided" )
48
+ return
49
+ }
50
+ a .container .Logger .Debugf ("adding %d gRPC server options" , len (grpcOpts ))
45
51
a .grpcServer .options = append (a .grpcServer .options , grpcOpts ... )
46
52
}
47
53
48
- // AddGRPCUnaryInterceptors allows users to add custom gRPC interceptors.
54
+ // AddGRPCUnaryInterceptors allows users to add custom gRPC interceptors.
49
55
// Example:
50
56
//
51
57
// func loggingInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
@@ -55,14 +61,34 @@ func (a *App) AddGRPCServerOptions(grpcOpts ...grpc.ServerOption) {
55
61
// }
56
62
// app.AddGRPCUnaryInterceptors(loggingInterceptor)
57
63
func (a * App ) AddGRPCUnaryInterceptors (interceptors ... grpc.UnaryServerInterceptor ) {
58
- a .grpcServer .interceptors = append (a .grpcServer .interceptors , interceptors ... )
64
+ if len (interceptors ) == 0 {
65
+ a .container .Logger .Debug ("no unary interceptors provided" )
66
+ return
67
+ }
68
+
69
+ a .container .Logger .Debugf ("adding %d valid unary interceptors" , len (interceptors ))
70
+ a .grpcServer .interceptors = append (a .grpcServer .interceptors , interceptors ... )
59
71
}
60
72
61
73
func (a * App ) AddGRPCServerStreamInterceptors (interceptors ... grpc.StreamServerInterceptor ) {
74
+ if len (interceptors ) == 0 {
75
+ a .container .Logger .Debug ("no stream interceptors provided" )
76
+ return
77
+ }
78
+
79
+ a .container .Logger .Debugf ("adding %d stream interceptors" , len (interceptors ))
62
80
a .grpcServer .streamInterceptors = append (a .grpcServer .streamInterceptors , interceptors ... )
63
81
}
64
82
65
- func newGRPCServer (c * container.Container , port int , cfg config.Config ) * grpcServer {
83
+ func newGRPCServer (c * container.Container , port int , cfg config.Config ) (* grpcServer , error ) {
84
+ if port <= 0 || port > 65535 {
85
+ return nil , fmt .Errorf ("invalid port number: %d" , port )
86
+ }
87
+
88
+ c .Logger .Infof ("creating new gRPC server on port %d" , port )
89
+
90
+ registerGRPCMetrics (c )
91
+
66
92
middleware := make ([]grpc.UnaryServerInterceptor , 0 )
67
93
middleware = append (middleware ,
68
94
grpc_recovery .UnaryServerInterceptor (),
@@ -72,32 +98,51 @@ func newGRPCServer(c *container.Container, port int, cfg config.Config) *grpcSer
72
98
streamMiddleware = append (streamMiddleware ,
73
99
grpc_recovery .StreamServerInterceptor (),
74
100
gofr_grpc .StreamObservabilityInterceptor (c .Logger , c .Metrics ()))
75
-
101
+
102
+ c .Logger .Debugf ("gRPC server created successfully on port %d" , port )
103
+
76
104
return & grpcServer {
77
105
port : port ,
78
106
interceptors : middleware ,
79
107
streamInterceptors : streamMiddleware ,
80
108
config : cfg ,
81
- }
109
+ }, nil
110
+ }
111
+
112
+ // registerGRPCMetrics registers essential gRPC metrics
113
+ func registerGRPCMetrics (c * container.Container ) {
114
+ c .Metrics ().NewGauge ("grpc_server_status" , "gRPC server status (1=running, 0=stopped)" )
115
+ c .Metrics ().NewCounter ("grpc_server_errors_total" , "Total gRPC server errors" )
116
+ c .Metrics ().NewCounter ("grpc_services_registered_total" , "Total gRPC services registered" )
82
117
}
83
118
84
- func (g * grpcServer ) createServer () {
119
+ func (g * grpcServer ) createServer () error {
85
120
interceptorOption := grpc .UnaryInterceptor (grpc_middleware .ChainUnaryServer (g .interceptors ... ))
86
121
streamOpt := grpc .StreamInterceptor (grpc_middleware .ChainStreamServer (g .streamInterceptors ... ))
87
122
g .options = append (g .options , interceptorOption , streamOpt )
88
123
89
124
g .server = grpc .NewServer (g .options ... )
125
+ if g .server == nil {
126
+ return errors .New ("failed to create gRPC server" )
127
+ }
90
128
91
129
enabled := strings .ToLower (g .config .GetOrDefault ("GRPC_ENABLE_REFLECTION" , "false" ))
92
- if enabled != defaultReflection {
93
- reflection .Register (g .server )
94
- }
130
+ if enabled == "true" {
131
+ reflection .Register (g .server )
132
+ }
133
+
134
+ return nil
95
135
}
96
136
97
137
func (g * grpcServer ) Run (c * container.Container ) {
138
+
98
139
if g .server == nil {
99
- g .createServer ()
100
- }
140
+ if err := g .createServer (); err != nil {
141
+ c .Logger .Fatalf ("failed to create gRPC server: %v" , err )
142
+ c .Metrics ().NewCounter ("grpc_server_errors_total" , "" ).Inc ()
143
+ return
144
+ }
145
+ }
101
146
102
147
addr := ":" + strconv .Itoa (g .port )
103
148
@@ -106,13 +151,22 @@ func (g *grpcServer) Run(c *container.Container) {
106
151
listener , err := net .Listen ("tcp" , addr )
107
152
if err != nil {
108
153
c .Logger .Errorf ("error in starting gRPC server at %s: %s" , addr , err )
154
+ c .Metrics ().NewCounter ("grpc_server_errors_total" , "" ).Inc ()
155
+ c .Metrics ().NewGauge ("grpc_server_status" , "" ).Set (0 )
109
156
return
110
157
}
158
+
159
+ c .Metrics ().NewGauge ("grpc_server_status" , "" ).Set (1 )
111
160
112
161
if err := g .server .Serve (listener ); err != nil {
113
162
c .Logger .Errorf ("error in starting gRPC server at %s: %s" , addr , err )
163
+ c .Metrics ().NewCounter ("grpc_server_errors_total" , "" ).Inc ()
164
+ c .Metrics ().NewGauge ("grpc_server_status" , "" ).Set (0 )
114
165
return
115
166
}
167
+
168
+ c .Logger .Infof ("gRPC server stopped on %s" , addr )
169
+ c .Metrics ().NewGauge ("grpc_server_status" , "" ).Set (0 )
116
170
}
117
171
118
172
func (g * grpcServer ) Shutdown (ctx context.Context ) error {
@@ -130,28 +184,35 @@ func (g *grpcServer) Shutdown(ctx context.Context) error {
130
184
}
131
185
132
186
var (
133
- errNonAddressable = errors .New ("cannot inject container as it is not addressable or is fail " )
187
+ errNonAddressable = errors .New ("cannot inject container as it is not addressable or is nil " )
134
188
)
135
189
136
190
// RegisterService adds a gRPC service to the GoFr application.
137
191
func (a * App ) RegisterService (desc * grpc.ServiceDesc , impl any ) {
192
+
138
193
if ! a .grpcRegistered && ! isPortAvailable (a .grpcServer .port ) {
139
194
a .container .Logger .Fatalf ("gRPC port %d is blocked or unreachable" , a .grpcServer .port )
140
195
}
141
196
142
- if ! a .grpcRegistered {
143
- a .grpcServer .createServer ()
144
- }
197
+ if ! a .grpcRegistered {
198
+ if err := a .grpcServer .createServer (); err != nil {
199
+ a .container .Logger .Errorf ("failed to create gRPC server for service %s: %v" , desc .ServiceName , err )
200
+ return
201
+ }
202
+ }
145
203
146
204
a .container .Logger .Infof ("registering gRPC Server: %s" , desc .ServiceName )
147
205
a .grpcServer .server .RegisterService (desc , impl )
148
206
207
+ a .container .Metrics ().NewCounter ("grpc_services_registered_total" , "" ).Inc ()
208
+
149
209
err := injectContainer (impl , a .container )
150
210
if err != nil {
151
- return
211
+ a . container . Logger . Fatalf ( "failed to inject container into gRPC service %s: %v" , desc . ServiceName , err )
152
212
}
153
213
154
214
a .grpcRegistered = true
215
+ a .container .Logger .Debugf ("successfully registered gRPC service: %s" , desc .ServiceName )
155
216
}
156
217
157
218
func injectContainer (impl any , c * container.Container ) error {
0 commit comments