-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add profiler server for pprof #591
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package api | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"net" | ||
"net/http" | ||
_ "net/http/pprof" | ||
"strconv" | ||
|
||
"github.com/rs/zerolog" | ||
) | ||
|
||
type ProfileServer struct { | ||
logger zerolog.Logger | ||
server *http.Server | ||
endpoint string | ||
} | ||
|
||
func NewProfileServer( | ||
logger zerolog.Logger, | ||
host string, | ||
port int, | ||
) *ProfileServer { | ||
endpoint := net.JoinHostPort(host, strconv.Itoa(port)) | ||
return &ProfileServer{ | ||
logger: logger, | ||
server: &http.Server{Addr: endpoint}, | ||
endpoint: endpoint, | ||
} | ||
} | ||
|
||
func (h *ProfileServer) ListenAddr() string { | ||
return h.endpoint | ||
} | ||
|
||
func (s *ProfileServer) Start() { | ||
go func() { | ||
err := s.server.ListenAndServe() | ||
if err != nil { | ||
if errors.Is(err, http.ErrServerClosed) { | ||
s.logger.Warn().Msg("Profiler server shutdown") | ||
return | ||
} | ||
s.logger.Err(err).Msg("failed to start Profiler server") | ||
panic(err) | ||
} | ||
}() | ||
} | ||
Comment on lines
+37
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider error handling improvement in Start method. The Start method correctly launches the server in a goroutine for non-blocking operation. However, panicking in case of a server start failure might not be the best approach in a production environment. Consider logging the error and returning it to the caller instead of panicking. This allows the caller to decide how to handle the error. Here's a suggested modification: func (s *ProfileServer) Start() error {
- go func() {
+ errCh := make(chan error, 1)
+ go func() {
err := s.server.ListenAndServe()
if err != nil && !errors.Is(err, http.ErrServerClosed) {
s.logger.Err(err).Msg("failed to start Profiler server")
- panic(err)
+ errCh <- err
}
+ close(errCh)
}()
+
+ // Wait a short time to catch immediate failures
+ select {
+ case err := <-errCh:
+ return err
+ case <-time.After(100 * time.Millisecond):
+ return nil
+ }
} This change allows the method to return any immediate errors while still running the server in a goroutine.
|
||
|
||
func (s *ProfileServer) Stop() error { | ||
ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout) | ||
defer cancel() | ||
|
||
return s.server.Shutdown(ctx) | ||
} | ||
Comment on lines
+51
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Define shutdownTimeout constant. The Stop method correctly uses a context with timeout for graceful shutdown. However, the Please define the const shutdownTimeout = 5 * time.Second Adjust the duration as appropriate for your use case. |
||
|
||
func (s *ProfileServer) Close() error { | ||
return s.server.Close() | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -52,6 +52,7 @@ type Bootstrap struct { | |||||||||||||||||||||
metrics *metrics.Server | ||||||||||||||||||||||
events *ingestion.Engine | ||||||||||||||||||||||
traces *traces.Engine | ||||||||||||||||||||||
profiler *api.ProfileServer | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
func New(config *config.Config) (*Bootstrap, error) { | ||||||||||||||||||||||
|
@@ -340,6 +341,38 @@ func (b *Bootstrap) StopMetricsServer() { | |||||||||||||||||||||
b.metrics.Stop() | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
func (b *Bootstrap) StartProfilerServer(_ context.Context) error { | ||||||||||||||||||||||
if !b.config.ProfilerEnabled { | ||||||||||||||||||||||
return nil | ||||||||||||||||||||||
} | ||||||||||||||||||||||
b.logger.Info().Msg("bootstrap starting profiler server") | ||||||||||||||||||||||
|
||||||||||||||||||||||
b.profiler = api.NewProfileServer(b.logger, b.config.ProfilerHost, b.config.ProfilerPort) | ||||||||||||||||||||||
|
||||||||||||||||||||||
b.profiler.Start() | ||||||||||||||||||||||
b.logger.Info().Msgf("Profiler server started: %s", b.profiler.ListenAddr()) | ||||||||||||||||||||||
|
||||||||||||||||||||||
return nil | ||||||||||||||||||||||
Comment on lines
+352
to
+355
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle potential error from The method Apply this diff to handle the error: b.profiler = api.NewProfileServer(b.logger, b.config.ProfilerHost, b.config.ProfilerPort)
- b.profiler.Start()
+ if err := b.profiler.Start(); err != nil {
+ return fmt.Errorf("failed to start profiler server: %w", err)
+ }
b.logger.Info().Msgf("Profiler server started: %s", b.profiler.ListenAddr()) 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
func (b *Bootstrap) StopProfilerServer() { | ||||||||||||||||||||||
if b.profiler == nil { | ||||||||||||||||||||||
return | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
b.logger.Warn().Msg("shutting down profiler server") | ||||||||||||||||||||||
|
||||||||||||||||||||||
err := b.profiler.Stop() | ||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||
if errors.Is(err, context.DeadlineExceeded) { | ||||||||||||||||||||||
b.logger.Warn().Msg("Profiler server graceful shutdown timed out") | ||||||||||||||||||||||
b.profiler.Close() | ||||||||||||||||||||||
} else { | ||||||||||||||||||||||
b.logger.Err(err).Msg("Profiler server graceful shutdown failed") | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Specify log level when logging errors In line 371, the code uses Apply this diff to correct the log level: - b.logger.Err(err).Msg("Profiler server graceful shutdown failed")
+ b.logger.Error().Err(err).Msg("Profiler server graceful shutdown failed") 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
// startEngine starts provided engine and panics if there are startup errors. | ||||||||||||||||||||||
func startEngine( | ||||||||||||||||||||||
ctx context.Context, | ||||||||||||||||||||||
|
@@ -478,6 +511,10 @@ func Run(ctx context.Context, cfg *config.Config, ready chan struct{}) error { | |||||||||||||||||||||
return fmt.Errorf("failed to start metrics server: %w", err) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
if err := boot.StartProfilerServer(ctx); err != nil { | ||||||||||||||||||||||
return fmt.Errorf("failed to start profiler server: %w", err) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
// mark ready | ||||||||||||||||||||||
close(ready) | ||||||||||||||||||||||
|
||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Address potential API key exposure
The static analysis tool has flagged potential API key exposure in these lines. This reinforces the need to move sensitive information out of the Makefile.
To address this:
.env
file (if used) is added to.gitignore
.🧰 Tools
Gitleaks