-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
feat: expose startup message customization hooks #3824
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
base: main
Are you sure you want to change the base?
Conversation
Summary of ChangesHello @efectn, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request enhances the Fiber framework by providing robust customization options for the application's startup message. It moves beyond a static banner, allowing developers to dynamically inject relevant information, warnings, or errors directly into the console output before and after the server starts listening. This improves the developer experience by making the startup process more informative and adaptable to specific application needs. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
WalkthroughImplements startup message customization through two new hooks ( Changes
Sequence Diagram(s)sequenceDiagram
participant App as App
participant PreHook as OnPreStartupMessage
participant Startup as Startup Flow
participant PostHook as OnPostStartupMessage
rect rgb(200, 220, 255)
Note over App,PostHook: New Startup Message Flow
end
App->>App: prepareListenData() → ListenData<br/>(enriched metadata)
App->>PreHook: executeOnPreStartupMessageHooks(data)
alt PreventDefault Set
PreHook-->>App: Prevent default banner
else Default Behavior
PreHook-->>App: Allow/customize banner
end
rect rgb(240, 255, 240)
App->>Startup: printMessages(listenData)<br/>renders startup banner
end
App->>PostHook: executeOnPostStartupMessageHooks(data)
alt DisableStartupMessage or Prevented
PostHook-->>App: Post-hook data (disabled=true)
else Normal Completion
PostHook-->>App: Post-hook data with status
end
rect rgb(255, 240, 240)
App->>App: startupProcess() completes<br/>(return removed)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes
Areas requiring extra attention:
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3824 +/- ##
==========================================
+ Coverage 91.73% 91.77% +0.04%
==========================================
Files 113 115 +2
Lines 11966 9781 -2185
==========================================
- Hits 10977 8977 -2000
+ Misses 727 529 -198
- Partials 262 275 +13
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
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.
Pull Request Overview
This PR refactors the startup message system by introducing customizable startup message hooks and expanding listener metadata. The changes improve extensibility by allowing users to customize the startup banner through OnPreStartupMessage and OnPostStartupMessage hooks, while also enriching the ListenData structure with comprehensive runtime metadata.
- Introduced
OnPreStartupMessageandOnPostStartupMessagehooks for startup banner customization - Expanded
ListenDatawith version, handler count, process count, PID, and child PID metadata - Refactored
startupMessage()to useListenDatainstead of individual parameters and process startup entries dynamically
Reviewed Changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| hooks.go | Added new hook types and data structures for startup message customization with entry management methods |
| listen.go | Refactored startupMessage() to use ListenData and support customizable startup entries, removed trailing whitespace from ASCII art |
| prefork.go | Updated to use childPIDs slice instead of string concatenation, integrated with new startup message flow |
| app.go | Changed startupProcess() return type from *App to void |
| hooks_test.go | Added comprehensive tests for startup message customization and ListenData metadata verification |
| listen_test.go | Updated test calls to use new startupMessage() signature with prepareListenData() |
| prefork_test.go | Updated test to use new startupMessage() signature |
| docs/whats_new.md | Documented new startup message customization features |
| docs/api/hooks.md | Added documentation for ListenData fields and startup message customization API |
| sm.PrimaryInfo = fiber.Map{"Git hash": os.Getenv("GIT_HASH")} | ||
| sm.SecondaryInfo = fiber.Map{"Process count": sm.ProcessCount} |
Copilot
AI
Oct 28, 2025
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.
The documentation references PrimaryInfo and SecondaryInfo fields that do not exist in the PreStartupMessageData struct. According to the actual implementation in hooks.go, users should call methods like AddInfo(), AddWarning(), AddError(), ResetEntries(), and DeleteEntry() to customize the startup entries. Update the example to use the correct API: sm.AddInfo(key, title, value, priority).
| sm.PrimaryInfo = fiber.Map{"Git hash": os.Getenv("GIT_HASH")} | |
| sm.SecondaryInfo = fiber.Map{"Process count": sm.ProcessCount} | |
| sm.AddInfo("git_hash", "Git hash", os.Getenv("GIT_HASH"), fiber.PriorityHigh) | |
| sm.AddInfo("process_count", "Process count", sm.ProcessCount, fiber.PriorityLow) |
| Use `OnPreStartupMessage` to tweak the banner before Fiber prints it, and `OnPostStartupMessage` to run logic after the banner is printed (or skipped): | ||
|
|
||
| - Assign `sm.Header` to override the ASCII art banner. Leave it empty to use the default. | ||
| - Provide `sm.PrimaryInfo` and/or `sm.SecondaryInfo` maps to replace the primary (server URL, handler counts, etc.) and secondary (prefork status, PID, process count) sections. |
Copilot
AI
Oct 28, 2025
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.
The documentation incorrectly describes PrimaryInfo and SecondaryInfo fields that don't exist. The actual API uses methods AddInfo(), AddWarning(), AddError(), ResetEntries(), and DeleteEntry() to manage startup entries. Update this documentation to reflect the actual implementation.
| sm.PrimaryInfo = fiber.Map{"Git hash": os.Getenv("GIT_HASH")} | ||
| sm.SecondaryInfo = fiber.Map{"Prefork": sm.Prefork} |
Copilot
AI
Oct 28, 2025
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.
The example code references non-existent fields. Replace with the actual API methods: sm.AddInfo(\"git_hash\", \"Git hash\", os.Getenv(\"GIT_HASH\"), priority) and similar calls for other entries.
| sm.PrimaryInfo = fiber.Map{"Git hash": os.Getenv("GIT_HASH")} | |
| sm.SecondaryInfo = fiber.Map{"Prefork": sm.Prefork} | |
| sm.AddInfo("git_hash", "Git hash", os.Getenv("GIT_HASH"), fiber.InfoPrimary) | |
| sm.AddInfo("prefork", "Prefork", sm.Prefork, fiber.InfoSecondary) |
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.
Code Review
This pull request introduces a great feature for customizing the startup message in Fiber, providing more flexibility for developers. The implementation with OnPreStartupMessage and OnPostStartupMessage hooks and the associated data structures is well-designed. The refactoring in listen.go and prefork.go to support this is clean.
However, there are some critical issues with the documentation. The examples in docs/api/hooks.md and docs/whats_new.md are based on an outdated implementation and will not work with the code in this PR. I've provided suggestions to fix them. I also found some dead code in hooks.go that should be removed.
Once these issues are addressed, this will be a solid contribution to Fiber.
| - Assign `sm.Header` to override the ASCII art banner. Leave it empty to use the default. | ||
| - Provide `sm.PrimaryInfo` and/or `sm.SecondaryInfo` maps to replace the primary (server URL, handler counts, etc.) and secondary (prefork status, PID, process count) sections. | ||
| - Set `sm.PreventDefault = true` to suppress the built-in banner without affecting other hooks. | ||
| - `PostStartupMessageData` reports whether the banner was skipped via the `Disabled`, `IsChild`, and `Prevented` flags. | ||
|
|
||
| ```go title="Customize the startup message" | ||
| package main | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "os" | ||
|
|
||
| "github.com/gofiber/fiber/v3" | ||
| ) | ||
|
|
||
| func main() { | ||
| app := fiber.New() | ||
|
|
||
| app.Hooks().OnPreStartupMessage(func(sm *fiber.PreStartupMessageData) error { | ||
| sm.Header = "FOOBER " + sm.Version + "\n-------" | ||
| sm.PrimaryInfo = fiber.Map{"Git hash": os.Getenv("GIT_HASH")} | ||
| sm.SecondaryInfo = fiber.Map{"Prefork": sm.Prefork} | ||
| return nil | ||
| }) | ||
|
|
||
| app.Hooks().OnPostStartupMessage(func(sm fiber.PostStartupMessageData) error { | ||
| if !sm.Disabled && !sm.IsChild && !sm.Prevented { | ||
| fmt.Println("startup completed") | ||
| } | ||
| return nil | ||
| }) | ||
|
|
||
| app.Listen(":5000") | ||
| } | ||
| ``` |
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.
The documentation for startup message customization is outdated. The description mentions sm.PrimaryInfo and sm.SecondaryInfo, and the example code uses them, but these fields do not exist on the PreStartupMessageData struct. The new API uses methods like AddInfo, AddWarning, and AddError. The documentation and example should be updated to reflect the correct API usage, as shown in the pull request description.
- Use `sm.AddInfo()`, `sm.AddWarning()`, and `sm.AddError()` to add, update, or remove startup message entries.
- Assign `sm.Header` to override the ASCII art banner. Leave it empty to use the default.
- Set `sm.PreventDefault = true` to suppress the built-in banner without affecting other hooks.
- `PostStartupMessageData` reports whether the banner was skipped via the `Disabled`, `IsChild`, and `Prevented` flags.
```go title="Customize the startup message"
package main
import (
"fmt"
"github.com/gofiber/fiber/v3"
)
func main() {
app := fiber.New()
app.Hooks().OnPreStartupMessage(func(sm *fiber.PreStartupMessageData) error {
sm.Header = "My Awesome App v" + sm.Version + "\n----------------------"
// You can add custom info, warnings, or errors to the startup message.
sm.AddInfo("custom_info", "Custom Info", "This is a custom message.")
sm.AddWarning("custom_warning", "Custom Warning", "This is a custom warning.")
// Entries can be prioritized. Higher priority entries are shown first.
sm.AddError("custom_error", "Custom Error", "This is a custom error.", 10)
return nil
})
app.Hooks().OnPostStartupMessage(func(sm fiber.PostStartupMessageData) error {
if !sm.Disabled && !sm.IsChild && !sm.Prevented {
fmt.Println("startup completed")
}
return nil
})
app.Listen(":5000")
}| app.Hooks().OnPreStartupMessage(func(sm *fiber.PreStartupMessageData) error { | ||
| sm.Header = "FOOBER " + sm.Version + "\n-------" | ||
| sm.PrimaryInfo = fiber.Map{"Git hash": os.Getenv("GIT_HASH")} | ||
| sm.SecondaryInfo = fiber.Map{"Process count": sm.ProcessCount} | ||
| // Set sm.PreventDefault = true to suppress the default banner entirely. | ||
| return nil | ||
| }) |
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.
The example code for the new startup message hooks uses sm.PrimaryInfo and sm.SecondaryInfo, which are from an older implementation and no longer exist on PreStartupMessageData. The new API uses methods like AddInfo. This example should be updated to reflect the correct usage.
| app.Hooks().OnPreStartupMessage(func(sm *fiber.PreStartupMessageData) error { | |
| sm.Header = "FOOBER " + sm.Version + "\n-------" | |
| sm.PrimaryInfo = fiber.Map{"Git hash": os.Getenv("GIT_HASH")} | |
| sm.SecondaryInfo = fiber.Map{"Process count": sm.ProcessCount} | |
| // Set sm.PreventDefault = true to suppress the default banner entirely. | |
| return nil | |
| }) | |
| app.Hooks().OnPreStartupMessage(func(sm *fiber.PreStartupMessageData) error { | |
| sm.Header = "FOOBER " + sm.Version + "\n-------" | |
| sm.AddInfo("git_hash", "Git hash", os.Getenv("GIT_HASH")) | |
| sm.AddInfo("process_count", "Process count", fmt.Sprintf("%d", sm.ProcessCount)) | |
| // Set sm.PreventDefault = true to suppress the default banner entirely. | |
| return nil | |
| }) |
| func mapToEntries(values Map) ([]startupMessageEntry, bool) { | ||
| if len(values) == 0 { | ||
| return nil, false | ||
| } | ||
|
|
||
| keys := make([]string, 0, len(values)) | ||
| for key := range values { | ||
| keys = append(keys, key) | ||
| } | ||
|
|
||
| sort.Strings(keys) | ||
|
|
||
| entries := make([]startupMessageEntry, 0, len(values)) | ||
| for _, key := range keys { | ||
| entries = append(entries, startupMessageEntry{key: key, value: fmt.Sprint(values[key])}) | ||
| } | ||
|
|
||
| return entries, true | ||
| } |
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.
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.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
listen.go (1)
53-56: Docstring nit: field name mismatchComment says “ListenerFunc” but the field is ListenerAddrFunc. Update the comment to match the field name to avoid confusion.
-// ListenerFunc allows accessing and customizing net.Listener. +// ListenerAddrFunc allows accessing and customizing net.Listener address.
🧹 Nitpick comments (4)
hooks_test.go (1)
292-352: Harden assertions and avoid unexported fields in tests
- Prefer public API: use app.Config().ColorScheme instead of app.config.ColorScheme.
- Guard slice access before indexing pre.entries.
Apply:
- require.Equal(t, app.config.ColorScheme, data.ColorScheme) + require.Equal(t, app.Config().ColorScheme, data.ColorScheme) @@ - require.Equal(t, app.config.ColorScheme, data.ColorScheme) + require.Equal(t, app.Config().ColorScheme, data.ColorScheme) @@ - require.Equal(t, "value", pre.entries[0].value) + require.Len(t, pre.entries, 2) + require.Equal(t, "value", pre.entries[0].value)prefork_test.go (1)
86-91: Unify stdout capture with captureOutput for consistencyReuse the existing captureOutput helper to simplify and ensure log output is captured identically across tests.
Example:
out := captureOutput(func() { app.startupProcess() listenData := app.prepareListenData(":0", false, cfg, nil) app.startupMessage(listenData, cfg) }) require.Empty(t, out)listen.go (2)
451-459: Avoid builtin min() for wider Go compatibilityIf the repo targets <1.21, builtin min won’t exist. Use explicit bounds check.
- end := min(i+rowTotalPidCount, totalPIDs) + end := i + rowTotalPidCount + if end > totalPIDs { + end = totalPIDs + }[Suggest running the same go.mod/version script shared above to confirm target Go version.]
111-125: Housekeeping: run format/align/modernizePlease run the repo’s formatting targets to align with project standards:
- make format (gofumpt)
- make betteralign
- make modernize (gopls)
As per coding guidelines.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
app.go(1 hunks)docs/api/hooks.md(1 hunks)docs/whats_new.md(1 hunks)hooks.go(6 hunks)hooks_test.go(2 hunks)listen.go(6 hunks)listen_test.go(5 hunks)prefork.go(3 hunks)prefork_test.go(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
docs/**
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Review and update the contents of the
docsfolder if necessary when modifying code
Files:
docs/api/hooks.mddocs/whats_new.md
**/*.go
📄 CodeRabbit inference engine (AGENTS.md)
**/*.go: Apply formatting using gofumpt (Make target: format)
Optimize struct field alignment using betteralign (Make target: betteralign)
Modernize code using gopls modernize (Make target: modernize)
Files:
app.gohooks_test.golisten_test.goprefork_test.gohooks.golisten.goprefork.go
🧠 Learnings (1)
📚 Learning: 2025-10-16T07:15:26.529Z
Learnt from: grivera64
PR: gofiber/fiber#3807
File: adapter_test.go:118-144
Timestamp: 2025-10-16T07:15:26.529Z
Learning: In Fiber v3, net/http handlers (http.Handler, http.HandlerFunc, or raw func(http.ResponseWriter, *http.Request)) can be passed directly to routing methods like app.Get(), app.Post(), etc. The framework automatically detects and wraps them internally via toFiberHandler/collectHandlers. The github.com/gofiber/fiber/v3/middleware/adaptor package is legacy and should not be suggested for tests or code using native net/http handler support.
Applied to files:
docs/whats_new.md
🧬 Code graph analysis (5)
hooks_test.go (3)
app.go (2)
Config(113-418)Version(37-37)listen.go (1)
ListenConfig(42-125)hooks.go (3)
Hooks(36-52)ListenData(75-90)PreStartupMessageData(93-101)
listen_test.go (3)
app.go (1)
New(515-628)listen.go (1)
ListenConfig(42-125)hooks.go (3)
Hooks(36-52)PreStartupMessageData(93-101)PostStartupMessageData(183-189)
prefork_test.go (1)
app.go (1)
New(515-628)
hooks.go (3)
color.go (1)
Colors(8-53)app.go (2)
Version(37-37)Map(43-43)prefork.go (1)
IsChild(31-33)
listen.go (4)
app.go (2)
App(68-110)Version(37-37)hooks.go (1)
ListenData(75-90)prefork.go (1)
IsChild(31-33)color.go (1)
Colors(8-53)
🪛 GitHub Actions: golangci-lint
hooks.go
[error] 93-93: golangci-lint (go vet) error: fieldalignment: struct with 40 pointer bytes could be 32 (govet)
🪛 GitHub Check: lint
hooks.go
[failure] 205-205:
func mapToEntries is unused (unused)
[failure] 93-93:
fieldalignment: struct with 40 pointer bytes could be 32 (govet)
listen.go
[failure] 475-475:
identical-switch-branches: "switch" with identical branches (lines 476 and 482) (revive)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Compare
- GitHub Check: unit (1.25.x, windows-latest)
- GitHub Check: repeated
🔇 Additional comments (11)
prefork.go (2)
92-121: LGTM: child PIDs propagation and hook orderingCollecting
childPIDsand feeding them intoprepareListenData, then runningOnListenbeforestartupMessageis correct and preserves master/child separation.Also applies to: 136-143
96-104: No issues found—Go version supportsfor rangeover intThe project's
go.modspecifiesgo 1.25.0, which fully supports thefor range intValuesyntax introduced in Go 1.22. The code atprefork.go:96is valid and requires no changes.hooks.go (1)
54-63: LGTM: startup message hook surface and executorsExported levels, extended ListenData, and pre/post executors look coherent and minimal.
Also applies to: 74-91, 401-419
app.go (1)
1328-1336: Verified: All call sites correctly updatedThe signature change is complete and consistent. Zero usages attempt to assign the return value from
startupProcess(). All 10 call sites treat it as a void function, confirming the refactoring is correct.listen_test.go (4)
627-663: Nice coverage of header and custom entriesCovers header override, entry reset, ordering, and post-hook flags. LGTM.
664-685: Disabled startup message + post-hook state — solidAsserts both no output and Disabled=true. LGTM.
686-713: Prevented-by-prehook path — good verificationValidates suppression and PostStartupMessage.Prevented. LGTM.
526-534: ****The repository is pinned to Go 1.25.0, making the
for range <int>syntax fully supported and idiomatic. The pattern used at line 528 (and elsewhere throughout the codebase) is appropriate and requires no changes. The original concern about Go 1.22-only syntax is not applicable.listen.go (3)
327-333: printMessages signature change — good separationDelegating to startupMessage(listenData, cfg) keeps concerns clean; routes printing remains gated. LGTM.
335-369: prepareListenData clones child PIDs and fills metadata correctlyNicely avoids aliasing and captures PID, handler/process counts, TLS/prefork flags. LGTM.
371-466: Startup message flow with pre/post hooks looks solid
- Honors DisableStartupMessage, IsChild, and PreventDefault.
- Always fires post-hook via defer.
One suggestion: compute disabled/isChild/prevented after pre-hook (as you do) and return early; already correct. LGTM.
| ### ListenData | ||
|
|
||
| `ListenData` exposes runtime metadata about the listener: | ||
|
|
||
| | Field | Type | Description | | ||
| | --- | --- | --- | | ||
| | `Host` | `string` | Resolved hostname or IP address. | | ||
| | `Port` | `string` | The bound port. | | ||
| | `TLS` | `bool` | Indicates whether TLS is enabled. | | ||
| | `Version` | `string` | Fiber version reported in the startup banner. | | ||
| | `AppName` | `string` | Application name from the configuration. | | ||
| | `HandlerCount` | `int` | Total registered handler count. | | ||
| | `ProcessCount` | `int` | Number of processes Fiber will use. | | ||
| | `PID` | `int` | Current process identifier. | | ||
| | `Prefork` | `bool` | Whether prefork is enabled. | | ||
| | `ChildPIDs` | `[]int` | Child process identifiers when preforking. | | ||
| | `ColorScheme` | [`Colors`](https://github.com/gofiber/fiber/blob/main/color.go) | Active color scheme for the startup message. | | ||
|
|
||
| ### Startup message customization | ||
|
|
||
| Use `OnPreStartupMessage` to tweak the banner before Fiber prints it, and `OnPostStartupMessage` to run logic after the banner is printed (or skipped): | ||
|
|
||
| - Assign `sm.Header` to override the ASCII art banner. Leave it empty to use the default. | ||
| - Provide `sm.PrimaryInfo` and/or `sm.SecondaryInfo` maps to replace the primary (server URL, handler counts, etc.) and secondary (prefork status, PID, process count) sections. | ||
| - Set `sm.PreventDefault = true` to suppress the built-in banner without affecting other hooks. | ||
| - `PostStartupMessageData` reports whether the banner was skipped via the `Disabled`, `IsChild`, and `Prevented` flags. | ||
|
|
||
| ```go title="Customize the startup message" | ||
| package main | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "os" | ||
|
|
||
| "github.com/gofiber/fiber/v3" | ||
| ) | ||
|
|
||
| func main() { | ||
| app := fiber.New() | ||
|
|
||
| app.Hooks().OnPreStartupMessage(func(sm *fiber.PreStartupMessageData) error { | ||
| sm.Header = "FOOBER " + sm.Version + "\n-------" | ||
| sm.PrimaryInfo = fiber.Map{"Git hash": os.Getenv("GIT_HASH")} | ||
| sm.SecondaryInfo = fiber.Map{"Prefork": sm.Prefork} | ||
| return nil | ||
| }) | ||
|
|
||
| app.Hooks().OnPostStartupMessage(func(sm fiber.PostStartupMessageData) error { | ||
| if !sm.Disabled && !sm.IsChild && !sm.Prevented { | ||
| fmt.Println("startup completed") | ||
| } | ||
| return nil | ||
| }) | ||
|
|
||
| app.Listen(":5000") | ||
| } | ||
| ``` |
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.
Docs: correct startup customization API usage
Replace references to PrimaryInfo/SecondaryInfo with the provided entry APIs and adjust the example.
-Use `OnPreStartupMessage` to tweak the banner before Fiber prints it, and `OnPostStartupMessage` to run logic after the banner is printed (or skipped):
-
-- Assign `sm.Header` to override the ASCII art banner. Leave it empty to use the default.
-- Provide `sm.PrimaryInfo` and/or `sm.SecondaryInfo` maps to replace the primary (server URL, handler counts, etc.) and secondary (prefork status, PID, process count) sections.
-- Set `sm.PreventDefault = true` to suppress the built-in banner without affecting other hooks.
-- `PostStartupMessageData` reports whether the banner was skipped via the `Disabled`, `IsChild`, and `Prevented` flags.
+Use `OnPreStartupMessage` to tweak the banner before Fiber prints it, and `OnPostStartupMessage` to run logic after the banner is printed (or skipped):
+
+- Set `sm.Header` to override the ASCII art/banner. Leave empty to keep the default.
+- Add lines with `sm.AddInfo`, `sm.AddWarning`, or `sm.AddError`. Use the optional `priority` arg to influence ordering (higher priority first).
+- Set `sm.PreventDefault = true` to suppress the built‑in banner.
+- `PostStartupMessageData` reports if the banner was disabled, printed by a child, or prevented via `Disabled`, `IsChild`, and `Prevented`.
@@
- app.Hooks().OnPreStartupMessage(func(sm *fiber.PreStartupMessageData) error {
- sm.Header = "FOOBER " + sm.Version + "\n-------"
- sm.PrimaryInfo = fiber.Map{"Git hash": os.Getenv("GIT_HASH")}
- sm.SecondaryInfo = fiber.Map{"Prefork": sm.Prefork}
- return nil
- })
+ app.Hooks().OnPreStartupMessage(func(sm *fiber.PreStartupMessageData) error {
+ sm.Header = "FOOBER " + sm.Version + "\n-------"
+ sm.AddInfo("git_hash", "Git hash", os.Getenv("GIT_HASH"))
+ sm.AddInfo("prefork", "Prefork", fmt.Sprint(sm.Prefork))
+ return nil
+ })
@@
- if !sm.Disabled && !sm.IsChild && !sm.Prevented {
- fmt.Println("startup completed")
- }
+ if !sm.Disabled && !sm.IsChild && !sm.Prevented {
+ fmt.Println("startup completed")
+ }
return nil
})Optionally document entry ordering/priority briefly.
🤖 Prompt for AI Agents
In docs/api/hooks.md around lines 171–227, the startup customization example and
description still reference sm.PrimaryInfo and sm.SecondaryInfo which are
incorrect; update the prose and code sample to use the hook's provided entry
APIs (e.g. call the API methods to add primary and secondary entries instead of
assigning maps), adjust the example to show calling those entry methods to set a
"Git hash" primary entry and a "Prefork" secondary entry, and optionally add one
short sentence noting that entries are rendered in the order they are added
(earlier entries have higher display priority).
| - Expanded `ListenData` with versioning, handler, process, and PID metadata, plus dedicated startup message hooks for customization. | ||
|
|
||
| ```go | ||
| app := fiber.New() | ||
|
|
||
| app.Hooks().OnPreStartupMessage(func(sm *fiber.PreStartupMessageData) error { | ||
| sm.Header = "FOOBER " + sm.Version + "\n-------" | ||
| sm.PrimaryInfo = fiber.Map{"Git hash": os.Getenv("GIT_HASH")} | ||
| sm.SecondaryInfo = fiber.Map{"Process count": sm.ProcessCount} | ||
| // Set sm.PreventDefault = true to suppress the default banner entirely. | ||
| return nil | ||
| }) | ||
|
|
||
| app.Hooks().OnPostStartupMessage(func(sm fiber.PostStartupMessageData) error { | ||
| if !sm.Disabled && !sm.IsChild && !sm.Prevented { | ||
| log.Println("startup completed") | ||
| } | ||
| return nil | ||
| }) | ||
|
|
||
| go app.Listen(":3000") | ||
| ``` |
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.
Align example with actual API (no PrimaryInfo/SecondaryInfo fields)
PreStartupMessageData exposes AddInfo/AddWarning/AddError and Header/PreventDefault, not PrimaryInfo/SecondaryInfo. Update the snippet to compile and reflect the API.
app := fiber.New()
app.Hooks().OnPreStartupMessage(func(sm *fiber.PreStartupMessageData) error {
- sm.Header = "FOOBER " + sm.Version + "\n-------"
- sm.PrimaryInfo = fiber.Map{"Git hash": os.Getenv("GIT_HASH")}
- sm.SecondaryInfo = fiber.Map{"Process count": sm.ProcessCount}
+ sm.Header = "FOOBER " + sm.Version + "\n-------"
+ sm.AddInfo("git_hash", "Git hash", os.Getenv("GIT_HASH"))
+ sm.AddInfo("proc_count", "Process count", fmt.Sprint(sm.ProcessCount))
// Set sm.PreventDefault = true to suppress the default banner entirely.
return nil
})
app.Hooks().OnPostStartupMessage(func(sm fiber.PostStartupMessageData) error {
if !sm.Disabled && !sm.IsChild && !sm.Prevented {
- log.Println("startup completed")
+ fmt.Println("startup completed")
}
return nil
})
go app.Listen(":3000")🤖 Prompt for AI Agents
In docs/whats_new.md around lines 298 to 319, the PreStartupMessageData example
uses nonexistent PrimaryInfo/SecondaryInfo fields and will not compile; replace
those with calls to AddInfo/AddWarning/AddError and set Header/PreventDefault as
shown by the API, e.g. populate the metadata using sm.AddInfo(key, value) (or
AddWarning/AddError) and remove PrimaryInfo/SecondaryInfo assignments, and
ensure the PostStartupMessageData checks use the actual fields
(Disabled/IsChild/Prevented) as in the snippet so the example matches the real
API.
| type PreStartupMessageData struct { | ||
| *ListenData | ||
|
|
||
| entries []startupMessageEntry | ||
|
|
||
| Header string | ||
|
|
||
| PreventDefault bool | ||
| } |
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.
Fix struct field alignment for PreStartupMessageData
Govet/betteralign flags padding waste. Reorder fields to reduce padding and satisfy the pipeline.
-type PreStartupMessageData struct {
- *ListenData
- entries []startupMessageEntry
- Header string
- PreventDefault bool
-}
+type PreStartupMessageData struct {
+ *ListenData
+ Header string
+ entries []startupMessageEntry
+ PreventDefault bool
+}As per coding guidelines (betteralign).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| type PreStartupMessageData struct { | |
| *ListenData | |
| entries []startupMessageEntry | |
| Header string | |
| PreventDefault bool | |
| } | |
| type PreStartupMessageData struct { | |
| *ListenData | |
| Header string | |
| entries []startupMessageEntry | |
| PreventDefault bool | |
| } |
🧰 Tools
🪛 GitHub Actions: golangci-lint
[error] 93-93: golangci-lint (go vet) error: fieldalignment: struct with 40 pointer bytes could be 32 (govet)
🪛 GitHub Check: lint
[failure] 93-93:
fieldalignment: struct with 40 pointer bytes could be 32 (govet)
🤖 Prompt for AI Agents
In hooks.go around lines 93 to 101, reorder the PreStartupMessageData struct
fields to eliminate padding waste: place the largest-aligned fields first
(entries slice), then Header (string), then the *ListenData pointer, and finally
PreventDefault (bool). Update the struct field order to: entries, Header,
*ListenData, PreventDefault and run go vet/go test to verify alignment fixes.
| func mapToEntries(values Map) ([]startupMessageEntry, bool) { | ||
| if len(values) == 0 { | ||
| return nil, false | ||
| } | ||
|
|
||
| keys := make([]string, 0, len(values)) | ||
| for key := range values { | ||
| keys = append(keys, key) | ||
| } | ||
|
|
||
| sort.Strings(keys) | ||
|
|
||
| entries := make([]startupMessageEntry, 0, len(values)) | ||
| for _, key := range keys { | ||
| entries = append(entries, startupMessageEntry{key: key, value: fmt.Sprint(values[key])}) | ||
| } | ||
|
|
||
| return entries, true | ||
| } |
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.
Remove or silence unused mapToEntries to fix lint failure
mapToEntries is unused and breaks CI. Either remove it now or add a targeted nolint with rationale.
Option A (remove):
-func mapToEntries(values Map) ([]startupMessageEntry, bool) {
- if len(values) == 0 {
- return nil, false
- }
- keys := make([]string, 0, len(values))
- for key := range values {
- keys = append(keys, key)
- }
- sort.Strings(keys)
- entries := make([]startupMessageEntry, 0, len(values))
- for _, key := range keys {
- entries = append(entries, startupMessageEntry{key: key, value: fmt.Sprint(values[key])})
- }
- return entries, true
-}Option B (silence temporarily):
- func mapToEntries(values Map) ([]startupMessageEntry, bool) {
+ // mapToEntries converts a map to sorted entries.
+ // nolint:unused // Will be wired by listen.go banner mapping follow-up.
+ func mapToEntries(values Map) ([]startupMessageEntry, bool) {Choose A if no immediate use is planned. As per coding guidelines.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| func mapToEntries(values Map) ([]startupMessageEntry, bool) { | |
| if len(values) == 0 { | |
| return nil, false | |
| } | |
| keys := make([]string, 0, len(values)) | |
| for key := range values { | |
| keys = append(keys, key) | |
| } | |
| sort.Strings(keys) | |
| entries := make([]startupMessageEntry, 0, len(values)) | |
| for _, key := range keys { | |
| entries = append(entries, startupMessageEntry{key: key, value: fmt.Sprint(values[key])}) | |
| } | |
| return entries, true | |
| } |
🧰 Tools
🪛 GitHub Check: lint
[failure] 205-205:
func mapToEntries is unused (unused)
🤖 Prompt for AI Agents
In hooks.go around lines 205 to 223, the helper function mapToEntries is
currently unused and causing CI lint failures; either delete the entire function
if it is not needed, or if you expect to use it imminently add a targeted nolint
(e.g., //nolint:unused // keep for upcoming startupMessage refactor) immediately
above the function and include a one‑sentence rationale, then run linters to
confirm the failure is resolved.
| childTemplate := []int{11111, 22222, 33333, 44444, 55555, 60000} | ||
| childPIDs := make([]int, 0, len(childTemplate)*10) | ||
| for range 10 { | ||
| childPIDs = append(childPIDs, childTemplate...) | ||
| } |
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.
Same here: replace Go 1.22-only for range 10
- for range 10 {
- childPIDs = append(childPIDs, childTemplate...)
- }
+ for i := 0; i < 10; i++ {
+ childPIDs = append(childPIDs, childTemplate...)
+ }🤖 Prompt for AI Agents
In listen_test.go around lines 560 to 564, the loop uses the Go 1.22-only
construct `for range 10`; replace it with a standard indexed loop such as `for i
:= 0; i < 10; i++ { childPIDs = append(childPIDs, childTemplate...) }` so the
code compiles on older Go versions and preserves the existing behavior and
preallocated capacity.
| func printStartupEntries(out io.Writer, colors Colors, entries []startupMessageEntry) { | ||
| // Sort entries by priority (higher priority first) | ||
| sort.Slice(entries, func(i, j int) bool { | ||
| return entries[i].priority > entries[j].priority | ||
| }) | ||
|
|
||
| for _, entry := range entries { | ||
| switch entry.level { | ||
| case StartupMessageLevelInfo: | ||
| fmt.Fprintf(out, "%sINFO%s %s: \t%s%s%s\n", colors.Green, colors.Reset, entry.title, colors.Blue, entry.value, colors.Reset) | ||
| case StartupMessageLevelWarning: | ||
| fmt.Fprintf(out, "%sWARN%s %s: \t%s%s%s\n", colors.Yellow, colors.Reset, entry.title, colors.Blue, entry.value, colors.Reset) | ||
| case StartupMessageLevelError: | ||
| fmt.Fprintf(out, "%sERROR%s %s: \t%s%s%s\n", colors.Red, colors.Reset, entry.title, colors.Blue, entry.value, colors.Reset) | ||
| default: | ||
| fmt.Fprintf(out, "%sINFO%s %s: \t%s%s%s\n", colors.Green, colors.Reset, entry.title, colors.Blue, entry.value, colors.Reset) | ||
| } | ||
| } | ||
| } |
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.
🛠️ Refactor suggestion | 🟠 Major
Fix identical switch branches; compute label/color once
Revive flags identical branches (info/default). Refactor to remove duplication.
-func printStartupEntries(out io.Writer, colors Colors, entries []startupMessageEntry) {
+func printStartupEntries(out io.Writer, colors Colors, entries []startupMessageEntry) {
// Sort entries by priority (higher priority first)
sort.Slice(entries, func(i, j int) bool {
return entries[i].priority > entries[j].priority
})
for _, entry := range entries {
- switch entry.level {
- case StartupMessageLevelInfo:
- fmt.Fprintf(out, "%sINFO%s %s: \t%s%s%s\n", colors.Green, colors.Reset, entry.title, colors.Blue, entry.value, colors.Reset)
- case StartupMessageLevelWarning:
- fmt.Fprintf(out, "%sWARN%s %s: \t%s%s%s\n", colors.Yellow, colors.Reset, entry.title, colors.Blue, entry.value, colors.Reset)
- case StartupMessageLevelError:
- fmt.Fprintf(out, "%sERROR%s %s: \t%s%s%s\n", colors.Red, colors.Reset, entry.title, colors.Blue, entry.value, colors.Reset)
- default:
- fmt.Fprintf(out, "%sINFO%s %s: \t%s%s%s\n", colors.Green, colors.Reset, entry.title, colors.Blue, entry.value, colors.Reset)
- }
+ label := "INFO"
+ color := colors.Green
+ switch entry.level {
+ case StartupMessageLevelWarning:
+ label, color = "WARN", colors.Yellow
+ case StartupMessageLevelError:
+ label, color = "ERROR", colors.Red
+ }
+ fmt.Fprintf(out, "%s%s%s %s: \t%s%s%s\n", color, label, colors.Reset, entry.title, colors.Blue, entry.value, colors.Reset)
}
}🧰 Tools
🪛 GitHub Check: lint
[failure] 475-475:
identical-switch-branches: "switch" with identical branches (lines 476 and 482) (revive)
🤖 Prompt for AI Agents
In listen.go around lines 468 to 486, the switch has duplicated branches (info
and default) printing identical output; refactor by having the switch only
assign the label string and its color (e.g., label="INFO"/"WARN"/"ERROR" and
labelColor=colors.Green/Yellow/Red) then after the switch perform a single
fmt.Fprintf that uses labelColor, colors.Reset, label, entry.title and
entry.value; ensure the default case maps to the info label/color and preserve
the Blue color for the value.
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.
⚠️ Performance Alert ⚠️
Possible performance regression was detected for benchmark.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.
| Benchmark suite | Current: a22dcdb | Previous: 83cc132 | Ratio |
|---|---|---|---|
Benchmark_Ctx_Write |
27.27 ns/op 79 B/op 0 allocs/op |
18.15 ns/op 69 B/op 0 allocs/op |
1.50 |
Benchmark_Ctx_Write - ns/op |
27.27 ns/op |
18.15 ns/op |
1.50 |
Benchmark_Ctx_SendString_B |
19.72 ns/op 0 B/op 0 allocs/op |
9.977 ns/op 0 B/op 0 allocs/op |
1.98 |
Benchmark_Ctx_SendString_B - ns/op |
19.72 ns/op |
9.977 ns/op |
1.98 |
This comment was automatically generated by workflow using github-action-benchmark.
|
@efectn can you check the AI hints and golang lint |
Description
This PR is successor of previous #3802 PR of @gaby to improve UX. Different than old PR, it gives ability to add messages with more user-friendly API like:
Fixes #3800
Changes introduced
Type of change
Checklist
Before you submit your pull request, please make sure you meet these requirements:
/docs/directory for Fiber's documentation.Commit formatting
Please use emojis in commit messages for an easy way to identify the purpose or intention of a commit. Check out the emoji cheatsheet here: CONTRIBUTING.md