Skip to content

feat: expose startup message customization hooks#3824

Merged
ReneWerner87 merged 29 commits intomainfrom
add-startup-message-hooks-to-fiber-2
Nov 19, 2025
Merged

feat: expose startup message customization hooks#3824
ReneWerner87 merged 29 commits intomainfrom
add-startup-message-hooks-to-fiber-2

Conversation

@efectn
Copy link
Member

@efectn efectn commented Oct 28, 2025

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:

func main() {
	app := fiber.New(fiber.Config{
		//MsgPackEncoder: msgpack.Marshal,
		//MsgPackDecoder: msgpack.Unmarshal,
	})

	app.Get("/", func(c fiber.Ctx) error {
		return c.SendString("Hello, World!")
	})

	app.Hooks().OnPreStartupMessage(func(sm *fiber.PreStartupMessageData) error {
		sm.AddInfo("custom_info", "Custom Info", "\tThis is a custom pre-startup message.")
		sm.AddWarning("custom_warning", "Custom Warning", "\tThis is a custom pre-startup warning.")
		sm.AddError("custom_error", "Custom Error", "\tThis is a custom pre-startup error.", 4)
		return nil
	})

	log.Fatal(app.Listen(":3000", fiber.ListenConfig{
		EnablePrefork: true,
	}))
}
image

Fixes #3800

Changes introduced

  • Documentation Update: Detail the updates made to the documentation and links to the changed files.
  • Changelog/What's New: Include a summary of the additions for the upcoming release notes.
  • Migration Guide: If necessary, provide a guide or steps for users to migrate their existing code to accommodate these changes.

Type of change

  • Enhancement (improvement to existing features and functionality)

Checklist

Before you submit your pull request, please make sure you meet these requirements:

  • Followed the inspiration of the Express.js framework for new functionalities, making them similar in usage.
  • Conducted a self-review of the code and provided comments for complex or critical parts.
  • Updated the documentation in the /docs/ directory for Fiber's documentation.
  • Added or updated unit tests to validate the effectiveness of the changes or new features.
  • Ensured that new and existing unit tests pass locally with the changes.
  • Verified that any new dependencies are essential and have been agreed upon by the maintainers/community.
  • Aimed for optimal performance with minimal allocations in the new code.
  • Provided benchmarks for the new code to analyze and improve upon.

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

Copilot AI review requested due to automatic review settings October 28, 2025 19:36
@efectn efectn requested a review from a team as a code owner October 28, 2025 19:36
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @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

  • Customizable Startup Messages: Introduced OnPreStartupMessage and OnPostStartupMessage hooks, allowing developers to fully customize the application's startup banner. This includes modifying the header, adding custom informational, warning, or error messages, and even preventing the default banner from being displayed.
  • Expanded ListenData: The ListenData struct has been significantly expanded to provide more comprehensive runtime metadata, such as the Fiber version, application name, handler count, process count, PID, child PIDs (for prefork mode), and the active color scheme.
  • Structured Message Entries: The PreStartupMessageData struct now includes methods like AddInfo, AddWarning, and AddError to programmatically add structured entries to the startup message, offering fine-grained control over the displayed information.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Refactors startup/listen flow to centralize startup context in a pointer-based ListenData, adds OnPreStartupMessage/OnPostStartupMessage hooks and supporting types/helpers, changes child PID handling to []int, updates related method signatures (pointer *ListenData, startupProcess() no longer returns *App), and updates tests/docs.

Changes

Cohort / File(s) Summary
Hook Registration & Data Structures
hooks.go, hooks_test.go
Adds OnPreStartupMessageHandler/OnPostStartupMessageHandler, StartupMessageLevel constants, exported PreStartupMessageData/PostStartupMessageData, internal startup entry helpers, hook registration methods, executors, helper methods (AddInfo/AddWarning/AddError/EntryKeys/ResetEntries/DeleteEntry), and updates executeOnListenHooks to accept *ListenData. Tests expanded for helper behavior and error paths.
Startup Data Model & Rendering
listen.go, listen_test.go
Introduces enriched ListenData and moves prepareListenData to return *ListenData (accepts childPIDs []int), updates startupMessage/printMessages to accept *ListenData, integrates pre/post startup hooks into rendering, and rewrites startup printing to be data-driven. Tests updated to use prepareListenData → startupMessage flow.
Prefork & PID handling
prefork.go, prefork_test.go
Switches child PID tracking from []string to []int, removes string conversions, passes childPIDs into prepareListenData, and updates tests to prepare and pass listen data before startupMessage.
App Startup Flow
app.go
startupProcess() no longer returns *App (now void), reorders startup steps (ensure auto-head routes, per-sub-app ensureAutoHeadRoutes, mountStartupProcess, then build tree), and changes runOnListenHooks to accept *ListenData.
Tests: hooks/startup integration
hooks_test.go, listen_test.go, prefork_test.go
Adds and adjusts tests covering ListenData metadata, pre/post startup hook flows (prevent/disable behavior), helper functions, ordering, error paths, and use of prepareListenData.
Services logging minor change
services.go
When retrieving service state fails, display the actual error string in the state output instead of the literal "ERROR" while retaining error coloring.
Documentation & What's New
docs/api/hooks.md, docs/whats_new.md
Documents new hooks, PreStartupMessageData/PostStartupMessageData, expanded ListenData fields, helper methods, examples for banner customization, and startup hook flow.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant App as App
    participant Hooks as Hooks
    participant Renderer as StartupRenderer

    Note over App: prepareListenData(addr,isTLS,cfg,childPIDs) → *ListenData

    App->>Hooks: executeOnPreStartupMessageHooks(*PreStartupMessageData)
    alt Pre hook sets PreventDefault
        Hooks-->>App: PreventDefault=true
        Note right of App: Suppress rendering
    else Pre hook customizes header/entries
        Hooks-->>App: Modified PreStartupMessageData
    end

    App->>Renderer: printMessages(*ListenData) (render if not prevented/disabled)
    Renderer->>Hooks: executeOnPostStartupMessageHooks(*PostStartupMessageData)
    Hooks-->>App: PostStartupMessageData (Disabled / IsChild / Prevented)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • All call sites changed from value to pointer for ListenData (hooks, tests, internal executors).
    • startupProcess() return removal — callers must not rely on returned *App.
    • Prefork child PID propagation as []int and integer-to-string rendering spots.
    • Concurrency/mutation safety for PreStartupMessageData helper methods used by hooks.
    • Test adjustments that rely on prepareListenData lifecycle.

Possibly related PRs

Suggested reviewers

  • gaby
  • sixcolors

Poem

🐰 I hopped upon the startup tree,

I counted PIDs and set them free.
Pre and Post I softly tweak,
Banners bloom and entries speak.
Hop—new hooks make the startup sweet!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.81% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: expose startup message customization hooks' accurately summarizes the main feature addition—exposing hooks for customizing startup messages—which aligns with the core changes across all modified files.
Description check ✅ Passed The description includes a clear problem statement, references the predecessor PR, provides a concrete usage example with the new API, and marks documentation/changelog updates as done, though some checklist items remain unchecked.
Linked Issues check ✅ Passed The PR successfully implements the core objectives from issue #3800: exposing OnPreStartupMessage and OnPostStartupMessage hooks, extending ListenData with metadata (Version, AppName, HandlerCount, ProcessCount, PID, Prefork, ChildPIDs), adding AddInfo/AddWarning/AddError methods, and supporting PreventDefault functionality.
Out of Scope Changes check ✅ Passed All changes are directly related to the startup message customization feature: internal refactoring of startup flows, hook management, ListenData extension, and documentation updates. The error-message enhancement in services.go is a minor supporting change consistent with improving startup output.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add-startup-message-hooks-to-fiber-2

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf4e756 and 19554f2.

📒 Files selected for processing (1)
  • docs/api/hooks.md (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/api/hooks.md
⏰ 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). (5)
  • GitHub Check: Compare
  • GitHub Check: unit (1.25.x, macos-latest)
  • GitHub Check: lint
  • GitHub Check: unit (1.25.x, windows-latest)
  • GitHub Check: repeated

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Oct 28, 2025

Codecov Report

❌ Patch coverage is 94.41341% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.65%. Comparing base (2a73a39) to head (19554f2).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
listen.go 91.20% 6 Missing and 2 partials ⚠️
hooks.go 97.50% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3824      +/-   ##
==========================================
+ Coverage   91.59%   91.65%   +0.05%     
==========================================
  Files         115      115              
  Lines        9867     9967     +100     
==========================================
+ Hits         9038     9135      +97     
- Misses        528      532       +4     
+ Partials      301      300       -1     
Flag Coverage Δ
unittests 91.65% <94.41%> (+0.05%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

Copilot AI left a 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 OnPreStartupMessage and OnPostStartupMessage hooks for startup banner customization
  • Expanded ListenData with version, handler count, process count, PID, and child PID metadata
  • Refactored startupMessage() to use ListenData instead 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

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a 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.

Comment on lines +193 to +227
- 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")
}
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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")
}

Comment on lines +303 to +309
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
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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.

Suggested change
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
})

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 mismatch

Comment 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 consistency

Reuse 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 compatibility

If 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/modernize

Please 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

📥 Commits

Reviewing files that changed from the base of the PR and between 83cc132 and a22dcdb.

📒 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 docs folder if necessary when modifying code

Files:

  • docs/api/hooks.md
  • docs/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.go
  • hooks_test.go
  • listen_test.go
  • prefork_test.go
  • hooks.go
  • listen.go
  • prefork.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 ordering

Collecting childPIDs and feeding them into prepareListenData, then running OnListen before startupMessage is correct and preserves master/child separation.

Also applies to: 136-143


96-104: No issues found—Go version supports for range over int

The project's go.mod specifies go 1.25.0, which fully supports the for range intValue syntax introduced in Go 1.22. The code at prefork.go:96 is valid and requires no changes.

hooks.go (1)

54-63: LGTM: startup message hook surface and executors

Exported 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 updated

The 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 entries

Covers header override, entry reset, ordering, and post-hook flags. LGTM.


664-685: Disabled startup message + post-hook state — solid

Asserts both no output and Disabled=true. LGTM.


686-713: Prevented-by-prehook path — good verification

Validates 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 separation

Delegating to startupMessage(listenData, cfg) keeps concerns clean; routes printing remains gated. LGTM.


335-369: prepareListenData clones child PIDs and fills metadata correctly

Nicely 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.

Copy link
Contributor

@github-actions github-actions bot left a 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: 8a9167a Previous: db2576e Ratio
Benchmark_Utils_SortAcceptedTypes_Sorted 16.54 ns/op 0 B/op 0 allocs/op 10.75 ns/op 0 B/op 0 allocs/op 1.54
Benchmark_Utils_SortAcceptedTypes_Sorted - ns/op 16.54 ns/op 10.75 ns/op 1.54

This comment was automatically generated by workflow using github-action-benchmark.

@gaby gaby added the v3 label Oct 29, 2025
@ReneWerner87
Copy link
Member

@efectn can you check the AI hints and golang lint

@ReneWerner87 ReneWerner87 added this to v3 Oct 29, 2025
@ReneWerner87 ReneWerner87 added this to the v3 milestone Oct 29, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
hooks.go (1)

90-98: Fix struct field alignment for PreStartupMessageData.

The field alignment issue flagged in the previous review remains unaddressed. Please reorder fields as suggested to reduce padding and satisfy golangci-lint.

Apply this diff:

 type PreStartupMessageData struct {
 	*ListenData
-	Header string
-	entries []startupMessageEntry
+	Header         string
+	entries        []startupMessageEntry
 	PreventDefault bool
 }

As per coding guidelines (betteralign). Based on the previous review, the optimal order is: entries, Header, *ListenData, PreventDefault.

🧹 Nitpick comments (1)
hooks.go (1)

100-125: Consider extracting priority-parsing logic (optional).

The three methods (AddInfo, AddWarning, AddError) duplicate the priority extraction pattern. While this is acceptable for such short methods, you could optionally extract a helper to eliminate the duplication.

Example:

func (sm *PreStartupMessageData) AddInfo(key, title, value string, priority ...int) {
	sm.addEntry(key, title, value, parsePriority(priority), StartupMessageLevelInfo)
}

func (sm *PreStartupMessageData) AddWarning(key, title, value string, priority ...int) {
	sm.addEntry(key, title, value, parsePriority(priority), StartupMessageLevelWarning)
}

func (sm *PreStartupMessageData) AddError(key, title, value string, priority ...int) {
	sm.addEntry(key, title, value, parsePriority(priority), StartupMessageLevelError)
}

func parsePriority(priority []int) int {
	if len(priority) > 0 {
		return priority[0]
	}
	return -1
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a22dcdb and f57c4d4.

📒 Files selected for processing (2)
  • hooks.go (5 hunks)
  • listen.go (6 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.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:

  • listen.go
  • hooks.go
🧠 Learnings (2)
📚 Learning: 2025-10-22T13:48:12.485Z
Learnt from: CR
Repo: gofiber/fiber PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-22T13:48:12.485Z
Learning: Applies to **/*.go : Optimize struct field alignment using betteralign (Make target: betteralign)

Applied to files:

  • hooks.go
📚 Learning: 2025-10-16T07:15:26.529Z
Learnt from: grivera64
Repo: gofiber/fiber PR: 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:

  • hooks.go
🧬 Code graph analysis (2)
listen.go (5)
app.go (2)
  • App (68-110)
  • Version (37-37)
hooks.go (1)
  • ListenData (72-87)
log/fiberlog.go (1)
  • Errorf (49-51)
prefork.go (1)
  • IsChild (31-33)
color.go (1)
  • Colors (8-53)
hooks.go (3)
color.go (1)
  • Colors (8-53)
app.go (1)
  • Version (37-37)
prefork.go (1)
  • IsChild (31-33)
🔇 Additional comments (12)
listen.go (5)

12-12: LGTM!

The io import is properly used by printStartupEntries which accepts an io.Writer parameter.


327-333: LGTM!

The updated signature correctly accepts and propagates ListenData to startupMessage.


336-369: LGTM!

The function correctly populates ListenData with startup metadata. The childPIDs cloning (lines 351-354) safely prevents mutations of the caller's slice.


372-466: LGTM!

The refactored startup flow correctly uses the data-driven approach with ListenData. The deferred post-startup hook (lines 419-424) executes correctly even when the banner is suppressed (line 426-428), ensuring hook consistency.


468-486: LGTM! Previous issue resolved.

The identical switch branches issue from the previous review has been properly addressed. The function now sets default label/color before the switch and only handles warning/error cases explicitly, eliminating duplication.

hooks.go (7)

18-21: LGTM!

The hook signatures correctly use a pointer for OnPreStartupMessageHandler (allowing mutation) and a value for OnPostStartupMessageHandler (read-only metadata).


51-69: LGTM!

The StartupMessageLevel enum and internal startupMessageEntry struct are well-defined. Keeping startupMessageEntry unexported is appropriate as it's an implementation detail.


72-87: LGTM!

The enriched ListenData provides comprehensive startup metadata for hooks and startup message customization.


127-168: LGTM!

The entry manipulation methods are correctly implemented. ResetEntries efficiently reuses the underlying array, and addEntry properly handles both updates and insertions.


170-200: LGTM!

Both helper functions correctly clone ListenData and ChildPIDs, ensuring hooks cannot mutate the original data. The append([]int(nil), ...) idiom is idiomatic and efficient.


210-211: LGTM!

The new hook slice initializations are consistent with existing patterns.


262-274: LGTM!

The hook registration and execution methods follow the established patterns in the codebase and are correctly implemented with proper mutex protection.

Also applies to: 378-396

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
listen_test.go (1)

513-544: Prefork startup message expectations likely no longer match actual colored output

The assertions for the Prefork line:

  • Line [543]: require.Contains(t, startupMessage, fmt.Sprintf("Prefork: \t\t\t%sEnabled%s", colors.Blue, colors.Reset))
  • Line [624]: require.Contains(t, startupMessage, fmt.Sprintf("Prefork: \t\t\t%sDisabled%s", colors.Red, colors.Reset))

use a zero-valued Colors{} (so the expected substring is literally "Prefork: \t\t\tEnabled" / "Prefork: \t\t\tDisabled"), while startupMessage now formats this line via printStartupEntries with nested color codes:

  • It prints Prefork: \t then colors.Blue and then an entry.value that itself starts with \t\t and embeds its own color sequences.

As a result, there are color escape sequences interleaved between the tabs and the word Enabled/Disabled, so the raw substring "Prefork: \t\t\tEnabled" / "Prefork: \t\t\tDisabled" is very unlikely to exist, and these tests will probably fail even though the line is rendered correctly.

I’d recommend loosening these checks to assert on more robust tokens instead of exact tab layout, for example:

  • Check presence of "Prefork:" and "Enabled" / "Disabled" separately, or
  • Strip ANSI codes from startupMessage before matching and then assert on the desired layout.

This keeps alignment expectations testable without tightly coupling to the internal coloring/format string.

Also applies to: 599-625

♻️ Duplicate comments (1)
hooks.go (1)

162-179: Fix duplicate entries when reusing a key in addEntry

addEntry currently:

  • Scans existing sm.entries and mutates the first matching element (same key), but
  • Still appends a new startupMessageEntry unconditionally afterward.

Calling AddInfo/AddWarning/AddError with the same key will therefore produce duplicate rows with the last call’s value and level, which is surprising and contradicts the “key” semantics.

You can fix this by returning immediately after updating an existing entry so the append only happens for new keys:

 func (sm *PreStartupMessageData) addEntry(key, title, value string, priority int, level StartupMessageLevel) {
 	if sm.entries == nil {
 		sm.entries = make([]startupMessageEntry, 0)
 	}

 	for i, entry := range sm.entries {
 		if entry.key != key {
 			continue
 		}

 		sm.entries[i].value = value
 		sm.entries[i].title = title
 		sm.entries[i].level = level
 		sm.entries[i].priority = priority
-	}
-
-	sm.entries = append(sm.entries, startupMessageEntry{key: key, title: title, value: value, level: level, priority: priority})
+		return
+	}
+
+	sm.entries = append(sm.entries, startupMessageEntry{key: key, title: title, value: value, level: level, priority: priority})
 }
🧹 Nitpick comments (3)
services.go (1)

146-150: Remove redundant uppercasing.

For the non-error path, state is converted to uppercase on line 148, then uppercased again when printing on line 150. Consider removing the uppercasing on line 148 since line 150 already applies strings.ToUpper(state) for both paths.

Apply this diff to eliminate the redundancy:

 	} else {
 		stateColor = scheme.Blue
-		state = strings.ToUpper(state)
 	}
 	fmt.Fprintf(out, "%sINFO%s    🥡 %s[ %s ] %s%s\n", scheme.Green, scheme.Reset, stateColor, strings.ToUpper(state), srv.String(), scheme.Reset)
listen.go (1)

374-469: startupMessage / Child PID rendering and hook sequencing look correct

Key points in startupMessage:

  • Builds default entries (server address, app name, handlers, prefork status, PID, process count) into PreStartupMessageData before running pre-startup hooks, allowing hooks to mutate/replace them (ResetEntries, Add*, PreventDefault, custom BannerHeader).
  • Always computes disabled, isChild, and prevented and defers construction of PostStartupMessageData, so post hooks run even when the message is suppressed.
  • Skips printing when DisableStartupMessage is true, when running in a prefork child (IsChild()), or when a pre hook sets PreventDefault, matching the new tests.
  • Child PIDs block prints a single “Child PIDs” header and then formats PIDs in rows of up to 10 per line, which is reasonable for large prefork configurations.

Overall the control flow and output look consistent with the intended API and test coverage; no blocking issues here.

You could optionally factor the Child PIDs inner loop to avoid recomputing len(listenData.ChildPIDs[start:end]) on each iteration by assigning row := listenData.ChildPIDs[start:end] once and using len(row) in the inner loop.

hooks.go (1)

181-216: Clone helpers should deep-copy ListenData value, not just reuse the pointer

Both newPreStartupMessageData and newPostStartupMessageData do:

clone := listenData
if len(listenData.ChildPIDs) > 0 {
    clone.ChildPIDs = append([]int(nil), listenData.ChildPIDs...)
}
return &PreStartupMessageData{ListenData: clone}

and similarly for PostStartupMessageData.

Because clone is just another pointer to the same ListenData struct, assigning clone.ChildPIDs = ... mutates the original listenData’s ChildPIDs field as well, and all hooks see and share the same underlying struct. If the intent is to provide snapshot-like data to hooks (especially for post-startup), these helpers should deep-copy the struct value and then deep-copy the slice:

-func newPreStartupMessageData(listenData *ListenData) *PreStartupMessageData {
-	clone := listenData
-	if len(listenData.ChildPIDs) > 0 {
-		clone.ChildPIDs = append([]int(nil), listenData.ChildPIDs...)
-	}
-
-	return &PreStartupMessageData{ListenData: clone}
-}
+func newPreStartupMessageData(listenData *ListenData) *PreStartupMessageData {
+	if listenData == nil {
+		return &PreStartupMessageData{}
+	}
+
+	clone := *listenData
+	if len(clone.ChildPIDs) > 0 {
+		clone.ChildPIDs = append([]int(nil), clone.ChildPIDs...)
+	}
+
+	return &PreStartupMessageData{ListenData: &clone}
+}
@@
-func newPostStartupMessageData(listenData *ListenData, disabled, isChild, prevented bool) PostStartupMessageData {
-	clone := listenData
-	if len(listenData.ChildPIDs) > 0 {
-		clone.ChildPIDs = append([]int(nil), listenData.ChildPIDs...)
-	}
-
-	return PostStartupMessageData{
-		ListenData: clone,
-		Disabled:   disabled,
-		IsChild:    isChild,
-		Prevented:  prevented,
-	}
-}
+func newPostStartupMessageData(listenData *ListenData, disabled, isChild, prevented bool) PostStartupMessageData {
+	var clonePtr *ListenData
+	if listenData != nil {
+		clone := *listenData
+		if len(clone.ChildPIDs) > 0 {
+			clone.ChildPIDs = append([]int(nil), clone.ChildPIDs...)
+		}
+		clonePtr = &clone
+	}
+
+	return PostStartupMessageData{
+		ListenData: clonePtr,
+		Disabled:   disabled,
+		IsChild:    isChild,
+		Prevented:  prevented,
+	}
+}

This keeps hook-visible data stable even if the original ListenData is later mutated and avoids surprising cross-hook side effects.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0429b23 and 8a9167a.

📒 Files selected for processing (6)
  • docs/api/hooks.md (1 hunks)
  • docs/whats_new.md (1 hunks)
  • hooks.go (5 hunks)
  • listen.go (5 hunks)
  • listen_test.go (5 hunks)
  • services.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • docs/api/hooks.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/whats_new.md
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-11-13T11:34:38.503Z
Learnt from: efectn
Repo: gofiber/fiber PR: 3824
File: listen_test.go:560-564
Timestamp: 2025-11-13T11:34:38.503Z
Learning: The gofiber/fiber project only supports the latest Go versions, so Go 1.22+ syntax features (such as `for range N` integer range loops) are acceptable and preferred.

Applied to files:

  • listen_test.go
📚 Learning: 2025-09-18T00:32:59.671Z
Learnt from: sixcolors
Repo: gofiber/fiber PR: 0
File: :0-0
Timestamp: 2025-09-18T00:32:59.671Z
Learning: In Go 1.22, manual copying of loop variables (like `x := x`) is no longer necessary due to the fix for variable capture in closures. Each loop iteration now creates its own instance of the loop variable automatically.

Applied to files:

  • listen_test.go
📚 Learning: 2024-11-15T07:56:21.623Z
Learnt from: ReneWerner87
Repo: gofiber/fiber PR: 3161
File: app.go:923-932
Timestamp: 2024-11-15T07:56:21.623Z
Learning: In the Fiber framework, breaking changes are acceptable when moving from version 2 to version 3, including modifications to method signatures such as in the `Test` method in `app.go`.

Applied to files:

  • listen_test.go
📚 Learning: 2025-10-16T07:15:26.529Z
Learnt from: grivera64
Repo: gofiber/fiber PR: 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:

  • hooks.go
🧬 Code graph analysis (3)
listen.go (4)
app.go (2)
  • App (68-112)
  • Version (37-37)
hooks.go (3)
  • ListenData (74-89)
  • StartupMessageLevelWarning (57-57)
  • StartupMessageLevelError (59-59)
prefork.go (1)
  • IsChild (31-33)
color.go (1)
  • Colors (8-53)
listen_test.go (4)
app.go (1)
  • New (523-636)
listen.go (1)
  • ListenConfig (42-125)
hooks.go (3)
  • Hooks (33-49)
  • PreStartupMessageData (92-102)
  • PostStartupMessageData (191-202)
prefork.go (1)
  • IsChild (31-33)
hooks.go (3)
color.go (1)
  • Colors (8-53)
app.go (1)
  • Version (37-37)
prefork.go (1)
  • IsChild (31-33)
⏰ 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). (6)
  • GitHub Check: unit (1.25.x, macos-latest)
  • GitHub Check: unit (1.25.x, windows-latest)
  • GitHub Check: Compare
  • GitHub Check: repeated
  • GitHub Check: lint
  • GitHub Check: update_release_draft
🔇 Additional comments (8)
services.go (1)

144-144: No issues found — errString is properly defined.

errString is defined as a package-level constant at hooks.go:62 with value "ERROR", which is appropriate for the change on line 144 of services.go.

listen_test.go (3)

627-662: Good coverage of pre/post startup customization behavior

Test_StartupMessageCustomization nicely exercises the new API:

  • Uses OnPreStartupMessage to replace the banner and entries (ResetEntries, AddInfo) and verifies the default entries are gone.
  • Uses OnPostStartupMessage to validate the disabled/child/prevented flags.

The flow matches the implementation in startupMessage and provides solid regression coverage.


664-684: Disabled startup message + post hook behavior looks correct

Test_StartupMessageDisabledPostHook correctly checks that:

  • No startup output is emitted when DisableStartupMessage is true.
  • The PostStartupMessageData flags reflect Disabled=true, IsChild=false, Prevented=false.

This aligns with the early-return path in startupMessage while still exercising the deferred post-hook logic.


686-712: PreventDefault hook semantics are validated well

Test_StartupMessagePreventedByHook verifies the PreventDefault flag:

  • Pre hook sets data.PreventDefault = true.
  • No startup message is printed.
  • Post hook sees Prevented=true with Disabled=false and IsChild=false.

This matches the prevented calculation and guard in startupMessage and covers an important API usage.

listen.go (3)

158-249: Listen/Listener refactor to ListenData pipeline looks consistent

The changes in Listen and Listener:

  • Call app.startupProcess() as before.
  • Build a ListenData via prepareListenData(ln.Addr().String(), getTLSConfig(ln) != nil, &cfg, nil).
  • Run app.runOnListenHooks(&listenData) and app.printMessages(&cfg, &listenData).

This keeps the external behavior while centralizing startup metadata and makes it much easier for hooks/tests to reason about startup state. No functional issues spotted in this part.

Also applies to: 253-288


330-372: prepareListenData correctly normalizes host, TLS, and process metadata

prepareListenData:

  • Uses parseAddr to split host/port and falls back to 0.0.0.0 or [::1] based on ListenerNetwork, matching existing semantics.
  • Computes ProcessCount based on EnablePrefork (using runtime.GOMAXPROCS(0)), and tracks PID, TLS, Prefork, and HandlerCount.
  • Clones the childPIDs slice before storing it in ListenData to avoid aliasing the input slice.

This is a clean centralization of all values that feed into the startup banner and hooks.


471-491: printStartupEntries implementation matches lint expectations and simplifies labeling

printStartupEntries:

  • Sorts entries by priority descending, then prints them in order.
  • Computes label/color once per entry via a small switch and uses a single fmt.Fprintf, eliminating duplicated branches that lint flagged previously.

This is concise and easier to extend (e.g., new levels) without duplicating format strings.

hooks.go (1)

73-89: Startup hook surface and wiring look solid

The new pieces:

  • ListenData now carries ColorScheme, app/version metadata, process counts, PID, TLS/Prefork, and ChildPIDs, which is sufficient for startup-related hooks.
  • PreStartupMessageData and PostStartupMessageData embed *ListenData and expose:
    • Banner control (BannerHeader),
    • Entry management (AddInfo/AddWarning/AddError, EntryKeys, ResetEntries, DeleteEntry),
    • Control flags (PreventDefault, Disabled, IsChild, Prevented).
  • Hooks are registered via OnPreStartupMessage/OnPostStartupMessage and executed in executeOnPreStartupMessageHooks/executeOnPostStartupMessageHooks with short-circuit on first error.
  • executeOnListenHooks now takes a *ListenData and still passes a value into OnListenHandler, preserving the public handler signature.

The overall API is coherent, aligns with the tests in listen_test.go, and offers a flexible customization surface without breaking existing hook behavior.

Also applies to: 91-132, 190-201, 218-290, 396-424

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8a9167a and 6d249ae.

📒 Files selected for processing (1)
  • hooks.go (5 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-16T07:15:26.529Z
Learnt from: grivera64
Repo: gofiber/fiber PR: 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:

  • hooks.go
🧬 Code graph analysis (1)
hooks.go (3)
color.go (1)
  • Colors (8-53)
app.go (1)
  • Version (37-37)
prefork.go (1)
  • IsChild (31-33)
🔇 Additional comments (10)
hooks.go (10)

18-21: LGTM: Handler types are well-defined.

The new handler signatures are appropriate. The OnPreStartupMessageHandler takes a pointer to allow mutation of startup message data, while OnPostStartupMessageHandler takes a value for read-only access post-printing.


51-62: LGTM: Enum definition follows Go conventions.

The StartupMessageLevel enum and constants are well-structured. The levels (Info, Warning, Error) provide appropriate categorization for startup messages.


73-89: LGTM: ListenData appropriately extended.

The new fields provide comprehensive metadata for startup message customization, mixing presentation data (ColorScheme, AppName) with runtime information (PID, HandlerCount, ChildPIDs).


91-102: LGTM: PreStartupMessageData structure is sound.

The struct appropriately embeds *ListenData, provides banner customization via BannerHeader, manages entries privately, and allows suppressing the default message with PreventDefault. The field order appears to address the alignment concerns from previous reviews.


104-132: LGTM: Entry addition methods are consistent and well-designed.

The AddInfo, AddWarning, and AddError methods provide a clean API with optional priority. The variadic priority parameter with a default of -1 is a good design choice.


134-160: LGTM: Entry management methods are correctly implemented.

The methods handle their responsibilities well:

  • EntryKeys safely returns a new slice
  • ResetEntries uses the idiomatic [:0] pattern
  • DeleteEntry properly handles nil checks and removes the first matching entry

162-186: LGTM: Duplicate entry issue resolved.

The addEntry method now correctly returns after updating an existing entry (line 176), preventing the duplicate entry bug that was flagged in previous reviews. The logic properly handles both update and insert cases.


225-240: LGTM: Hook initialization is consistent.

The newHooks function properly initializes the new onPreStartup and onPostStartup slices alongside existing hooks.


285-297: LGTM: Hook registration methods follow established patterns.

Both OnPreStartupMessage and OnPostStartupMessage correctly implement thread-safe registration using mutexes and support variadic handler arguments for convenience.


403-431: LGTM: Hook execution methods are correctly implemented.

The execution methods properly match their handler signatures:

  • executeOnListenHooks dereferences the pointer for value-based handlers
  • executeOnPreStartupMessageHooks passes the pointer for mutable access
  • executeOnPostStartupMessageHooks passes the value for read-only access

Error propagation is consistent across all three methods.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0429b23 and 6d249ae.

📒 Files selected for processing (6)
  • docs/api/hooks.md (1 hunks)
  • docs/whats_new.md (1 hunks)
  • hooks.go (5 hunks)
  • listen.go (5 hunks)
  • listen_test.go (5 hunks)
  • services.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • listen_test.go
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-16T07:15:26.529Z
Learnt from: grivera64
Repo: gofiber/fiber PR: 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
  • hooks.go
📚 Learning: 2024-11-15T07:56:21.623Z
Learnt from: ReneWerner87
Repo: gofiber/fiber PR: 3161
File: app.go:923-932
Timestamp: 2024-11-15T07:56:21.623Z
Learning: In the Fiber framework, breaking changes are acceptable when moving from version 2 to version 3, including modifications to method signatures such as in the `Test` method in `app.go`.

Applied to files:

  • docs/whats_new.md
🧬 Code graph analysis (2)
listen.go (4)
app.go (2)
  • App (68-112)
  • Version (37-37)
hooks.go (1)
  • ListenData (74-89)
prefork.go (1)
  • IsChild (31-33)
color.go (1)
  • Colors (8-53)
hooks.go (3)
color.go (1)
  • Colors (8-53)
app.go (1)
  • Version (37-37)
prefork.go (1)
  • IsChild (31-33)
⏰ 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). (2)
  • GitHub Check: repeated
  • GitHub Check: Compare

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
hooks.go (2)

188-190: Fix shallow-copy bug: original ListenData can be mutated.

The constructor directly embeds the pointer without copying the struct. If a hook modifies fields in PreStartupMessageData.ListenData, it will mutate the original ListenData, breaking encapsulation and causing unexpected side effects.

Apply this diff to create a proper copy:

 func newPreStartupMessageData(listenData *ListenData) *PreStartupMessageData {
-	return &PreStartupMessageData{ListenData: listenData}
+	clone := *listenData
+	if len(listenData.ChildPIDs) > 0 {
+		clone.ChildPIDs = append([]int(nil), listenData.ChildPIDs...)
+	}
+	return &PreStartupMessageData{ListenData: &clone}
 }

Based on learnings from past review comments.


206-218: Fix shallow-copy bug: ChildPIDs slice shares underlying array.

Line 209 uses clone.ChildPIDs = clone.ChildPIDs[:] which creates a new slice header but still references the same underlying array. Modifications to the cloned slice's elements will affect the original ListenData.

Apply this diff to properly deep-copy the slice:

 func newPostStartupMessageData(listenData *ListenData, disabled, isChild, prevented bool) PostStartupMessageData {
 	clone := *listenData
 	if len(listenData.ChildPIDs) > 0 {
-		clone.ChildPIDs = clone.ChildPIDs[:] // make a copy
+		clone.ChildPIDs = append([]int(nil), listenData.ChildPIDs...)
 	}

 	return PostStartupMessageData{
 		ListenData: &clone,
 		Disabled:   disabled,
 		IsChild:    isChild,
 		Prevented:  prevented,
 	}
 }

Based on learnings from past review comments.

🧹 Nitpick comments (1)
hooks.go (1)

91-102: Note: Past review flagged field alignment issue.

A previous review suggested reordering fields to reduce padding waste. If the linter still flags this, consider reordering as: entries, BannerHeader, *ListenData, PreventDefault.

Based on learnings from past review comments.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6d249ae and c177a6a.

📒 Files selected for processing (4)
  • docs/api/hooks.md (1 hunks)
  • docs/whats_new.md (1 hunks)
  • hooks.go (5 hunks)
  • hooks_test.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/api/hooks.md
  • docs/whats_new.md
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-16T07:15:26.529Z
Learnt from: grivera64
Repo: gofiber/fiber PR: 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:

  • hooks.go
🧬 Code graph analysis (2)
hooks_test.go (3)
app.go (3)
  • New (523-636)
  • Config (115-426)
  • Version (37-37)
listen.go (1)
  • ListenConfig (42-125)
hooks.go (3)
  • Hooks (33-49)
  • ListenData (74-89)
  • PreStartupMessageData (92-102)
hooks.go (3)
color.go (1)
  • Colors (8-53)
app.go (1)
  • Version (37-37)
prefork.go (1)
  • IsChild (31-33)
⏰ 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). (6)
  • GitHub Check: Compare
  • GitHub Check: unit (1.25.x, macos-latest)
  • GitHub Check: repeated
  • GitHub Check: unit (1.25.x, windows-latest)
  • GitHub Check: lint
  • GitHub Check: update_release_draft
🔇 Additional comments (13)
hooks_test.go (4)

6-7: LGTM!

The new imports support validation of PID and process count metadata in the startup message tests.


292-352: LGTM!

Comprehensive test coverage for ListenData metadata and PreStartupMessageData manipulation. Validates all fields, hook execution, and entry management correctly.


354-492: LGTM!

Thorough test coverage of all PreStartupMessageData helper functions. The AddInfo-UpdateExisting subtest correctly verifies that duplicate keys update the existing entry rather than creating duplicates.


626-626: LGTM!

Correctly updated to pass a pointer to ListenData, matching the new executeOnListenHooks(*ListenData) signature.

hooks.go (9)

18-21: LGTM!

The handler signatures are correctly designed: OnPreStartupMessageHandler receives a pointer to allow mutation of the startup message, while OnPostStartupMessageHandler receives a value for read-only access.


43-44: LGTM!

The new hook slices follow the established pattern in the Hooks struct.


51-62: LGTM!

The StartupMessageLevel enumeration follows standard Go patterns.


64-71: LGTM!

The internal startupMessageEntry struct is appropriately designed for storing startup message data.


73-89: LGTM!

The expanded ListenData struct provides comprehensive metadata for startup hooks.


104-132: LGTM!

The Add* methods provide a clean, consistent API for adding startup message entries with optional priority.


134-160: LGTM!

The helper methods are correctly implemented. ResetEntries efficiently clears the slice while preserving capacity.


162-186: LGTM!

The addEntry method correctly returns after updating an existing entry (line 176), preventing duplicate entries. This addresses the issue flagged in previous reviews.


228-229: LGTM!

The hook registration and execution methods are correctly implemented:

  • Mutex locking protects concurrent access during registration
  • executeOnListenHooks dereferences the pointer to pass a copy to handlers (line 400), preventing mutation
  • executeOnPreStartupMessageHooks passes a pointer for mutation (line 410)
  • executeOnPostStartupMessageHooks passes a value for read-only access (line 420)

Also applies to: 280-292, 398-426

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
hooks_test.go (1)

354-519: Consider reducing white‑box coupling and strengthening AddWarning/AddError coverage

The helper-function subtests give nice coverage for EntryKeys, ResetEntries, AddInfo, AddWarning, AddError, update, and delete behavior. Two optional refinements:

  • The tests reach into data.entries and index into the slice directly. Since entries is an internal detail, you may want to assert via public helpers (e.g., EntryKeys plus a tiny lookup helper) to reduce coupling to the internal slice layout and ordering.
  • For AddWarning and AddError, you currently assert only key/title/value/priority, which is identical to AddInfo from the test’s point of view. If startupMessageEntry carries a message kind/severity, consider also asserting that the kind changes appropriately so regressions where warning/error fall back to info are caught.

These are minor and can be deferred, but would make the tests more robust against internal refactors.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c177a6a and 2ee4c6b.

📒 Files selected for processing (1)
  • hooks_test.go (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
hooks_test.go (3)
app.go (3)
  • New (523-636)
  • Config (115-426)
  • Version (37-37)
listen.go (1)
  • ListenConfig (42-125)
hooks.go (4)
  • Hooks (33-49)
  • ListenData (74-89)
  • PreStartupMessageData (92-102)
  • PostStartupMessageData (193-204)
⏰ 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). (6)
  • GitHub Check: unit (1.25.x, windows-latest)
  • GitHub Check: repeated
  • GitHub Check: unit (1.25.x, macos-latest)
  • GitHub Check: lint
  • GitHub Check: Compare
  • GitHub Check: update_release_draft
🔇 Additional comments (3)
hooks_test.go (3)

6-7: New imports are correctly scoped to metadata tests

os and runtime are used only in the new metadata test to assert PID and process count; the additions are appropriate and localized to tests.


292-352: ListenData/PreStartup metadata test looks solid

Test_ListenDataMetadata exercises prepareListenData, OnListen, and OnPreStartupMessage against the same ListenData instance, validating host/port, TLS, app name, handler count, process count, PID, prefork, child PIDs, and color scheme. This gives good coverage of the new metadata fields and pointer-based flow without introducing brittle assumptions.


645-679: Updated listen and startup hook error-path tests align with new APIs

  • Test_executeOnListenHooks_Error now passes a *ListenData into executeOnListenHooks, matching the updated signature while still asserting error propagation.
  • The new Test_executeOnPreStartupMessageHooks_Error and Test_executeOnPostStartupMessageHooks_Error verify that failures from pre/post-startup message hooks are surfaced by their respective executors.

These tests correctly exercise the error paths for the new hook APIs without introducing extra complexity.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
hooks.go (1)

20-23: Consider making handler signatures consistent.

OnPreStartupMessageHandler accepts a pointer (*PreStartupMessageData) while OnPostStartupMessageHandler accepts a value (PostStartupMessageData). The pointer allows pre-hooks to mutate startup data, but the asymmetry may confuse users. If post-hooks don't need mutation, document why; if both should be read-only after the fact, consider passing *PostStartupMessageData for symmetry with other hook pairs.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2ee4c6b and d5e5784.

📒 Files selected for processing (2)
  • hooks.go (6 hunks)
  • hooks_test.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • hooks_test.go
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-16T07:15:26.529Z
Learnt from: grivera64
Repo: gofiber/fiber PR: 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:

  • hooks.go
🧬 Code graph analysis (1)
hooks.go (3)
color.go (1)
  • Colors (8-53)
app.go (1)
  • Version (37-37)
prefork.go (1)
  • IsChild (31-33)
⏰ 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: repeated
  • GitHub Check: Compare
  • GitHub Check: update_release_draft
🔇 Additional comments (6)
hooks.go (6)

34-91: LGTM! Clean additions to support startup message hooks.

The new hook slices, StartupMessageLevel enum, startupMessageEntry internal struct, and expanded ListenData all follow existing patterns and provide a clear API surface for startup customization.


106-188: LGTM! Entry manipulation methods are correct.

The AddInfo/AddWarning/AddError methods properly handle optional priority, and the addEntry method now correctly returns after updating an existing entry (line 178), fixing the previous duplicate-entry bug. EntryKeys, ResetEntries, and DeleteEntry all use standard Go patterns.


190-192: Verify: Should PreStartupMessageData copy ListenData?

newPreStartupMessageData passes the original *ListenData pointer directly, allowing pre-hooks to mutate the original struct. In contrast, newPostStartupMessageData (lines 208-220) creates a defensive deep copy with clone := *listenData.

If pre-hooks should mutate shared state, the current design is correct. If they should receive isolated copies (similar to post-hooks), apply this pattern:

func newPreStartupMessageData(listenData *ListenData) *PreStartupMessageData {
	clone := *listenData
	if len(listenData.ChildPIDs) > 0 {
		clone.ChildPIDs = slices.Clone(listenData.ChildPIDs)
	}
	return &PreStartupMessageData{ListenData: &clone}
}

222-294: LGTM! Hook initialization and registration follow established patterns.

The newHooks constructor properly initializes the new hook slices, and OnPreStartupMessage/OnPostStartupMessage registration methods correctly use mutex locking consistent with other hook methods.


400-428: LGTM! Hook execution methods correctly match handler signatures.

executeOnListenHooks now accepts a pointer and dereferences it to match the value-based OnListenHandler signature. The new executeOnPreStartupMessageHooks and executeOnPostStartupMessageHooks correctly propagate pointer and value types respectively, matching their handler signatures.


93-104: No alignment issues detected—struct already satisfies field alignment requirements.

Verification using go vet -vet=all found no field alignment violations in PreStartupMessageData. The current field ordering does not trigger alignment warnings from Go's standard tooling. The concern raised in the original review is not present in the codebase.

@gaby
Copy link
Member

gaby commented Nov 15, 2025

@efectn thoughts on this:

hooks.go (1)
20-23: Consider making handler signatures consistent.

OnPreStartupMessageHandler accepts a pointer (*PreStartupMessageData) while OnPostStartupMessageHandler accepts a value (PostStartupMessageData). The pointer allows pre-hooks to mutate startup data, but the asymmetry may confuse users. If post-hooks don't need mutation, document why; if both should be read-only after the fact, consider passing *PostStartupMessageData for symmetry with other hook pairs

@gaby
Copy link
Member

gaby commented Nov 15, 2025

/gemini review

@gaby
Copy link
Member

gaby commented Nov 15, 2025

@codex review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a 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 an excellent feature for customizing startup messages, providing a much-improved user experience. The new hooks OnPreStartupMessage and OnPostStartupMessage are well-designed, and the API for adding, removing, and modifying startup message entries is intuitive. The associated refactoring to centralize listener data into the ListenData struct and pass it around by pointer has cleaned up the code in listen.go and prefork.go significantly. The documentation updates are comprehensive, and the new tests cover the functionality well. I have a couple of minor suggestions for further improvement, but overall, this is a high-quality contribution.

Comment on lines +354 to 357
var clonedPIDs []int
if len(childPIDs) > 0 {
clonedPIDs = append(clonedPIDs, childPIDs...)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For cloning the childPIDs slice, you can use slices.Clone(childPIDs) which is more idiomatic since Go 1.21. This would make the code more concise and align with modern Go practices. You'll need to add "slices" to the imports.

Suggested change
var clonedPIDs []int
if len(childPIDs) > 0 {
clonedPIDs = append(clonedPIDs, childPIDs...)
}
clonedPIDs := slices.Clone(childPIDs)

app.logServices(app.servicesStartupCtx(), out, &colors)

if listenData.Prefork && len(listenData.ChildPIDs) > 0 {
fmt.Fprintf(out, "%sINFO%s Child PIDs: \t\t%s", colors.Green, colors.Reset, colors.Blue)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The INFO label is hardcoded here. For consistency with printStartupEntries, it would be better to use the same mechanism to generate the label and color based on the message level. This would also make it easier to change the label style globally in the future.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@efectn are we forcing colors here? It should use the bool from the config to determine if colors are needed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not about colors. Prefork message is not added to startupMessageEntry like other messages. It should use app colors with no problem. Maybe i can add it to startupmessageentry just like other messages if it is necessary

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Hooray!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Member

@ReneWerner87 ReneWerner87 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good, can you check my comments


`MIMEApplicationJavaScript` and `MIMEApplicationJavaScriptCharsetUTF8` are deprecated. Use `MIMETextJavaScript` and `MIMETextJavaScriptCharsetUTF8` instead.

## 🎣 Hooks
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls extend this section with the 2 new hooks

@ReneWerner87
Copy link
Member

@efectn can you check my hints

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
hooks.go (2)

53-74: Pre-startup entry model is well-structured; fix minor doc mismatch

The StartupMessageLevel enum, startupMessageEntry, and PreStartupMessageData helpers (AddInfo, AddWarning, AddError, EntryKeys, ResetEntries, DeleteEntry, addEntry) are implemented correctly:

  • Optional priority defaults to -1, allowing ordering without forcing a value.
  • Re-adding the same key updates in place and avoids duplicates.
  • ResetEntries/DeleteEntry safely handle nil and non-nil slices.

One small nit: the AddWarning comment says it adds an entry with "WARNING" label, but printStartupEntries renders the label as "WARN". Consider aligning the comment with the actual label (or vice versa) to avoid confusion.

-// AddWarning adds a warning entry to the startup message with "WARNING" label.
+// AddWarning adds a warning entry to the startup message with a "WARN" label.

Also applies to: 93-188


190-220: Consider documenting or aligning Pre/Post ListenData mutability

newPreStartupMessageData exposes the original *ListenData to pre-startup hooks, while newPostStartupMessageData creates a struct copy and clones ChildPIDs before handing data to post-startup hooks. That means:

  • Pre hooks can mutate the original ListenData (and those changes are then visible to post hooks).
  • Post hooks work on an isolated copy and cannot affect internal state.

This asymmetry is probably intentional (pre hooks can influence rendering; post hooks are observational only), but it’s subtle. I’d either:

  • Add a brief comment to both constructors explaining the intended mutability semantics, or
  • Make newPreStartupMessageData mirror newPostStartupMessageData by cloning ListenData as well, if you prefer full isolation.
-func newPreStartupMessageData(listenData *ListenData) *PreStartupMessageData {
-	return &PreStartupMessageData{ListenData: listenData}
-}
+// newPreStartupMessageData constructs a pre-startup payload.
+// It intentionally exposes the original ListenData so hooks can
+// influence rendering (e.g., via PreventDefault or custom entries).
+func newPreStartupMessageData(listenData *ListenData) *PreStartupMessageData {
+	return &PreStartupMessageData{ListenData: listenData}
+}

(If you’d rather isolate pre hooks as well, mirror the newPostStartupMessageData cloning logic instead.)

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d5e5784 and dd0d8e0.

📒 Files selected for processing (7)
  • docs/api/hooks.md (3 hunks)
  • hooks.go (6 hunks)
  • hooks_test.go (3 hunks)
  • listen.go (5 hunks)
  • listen_test.go (5 hunks)
  • prefork.go (3 hunks)
  • prefork_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/api/hooks.md
  • listen_test.go
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-16T07:15:26.529Z
Learnt from: grivera64
Repo: gofiber/fiber PR: 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:

  • hooks.go
📚 Learning: 2024-11-15T07:56:21.623Z
Learnt from: ReneWerner87
Repo: gofiber/fiber PR: 3161
File: app.go:923-932
Timestamp: 2024-11-15T07:56:21.623Z
Learning: In the Fiber framework, breaking changes are acceptable when moving from version 2 to version 3, including modifications to method signatures such as in the `Test` method in `app.go`.

Applied to files:

  • prefork_test.go
🧬 Code graph analysis (4)
hooks_test.go (3)
app.go (2)
  • Config (115-426)
  • Version (37-37)
listen.go (1)
  • ListenConfig (43-126)
hooks.go (4)
  • Hooks (35-51)
  • ListenData (76-91)
  • PreStartupMessageData (94-104)
  • PostStartupMessageData (195-206)
listen.go (4)
app.go (2)
  • App (68-112)
  • Version (37-37)
hooks.go (1)
  • ListenData (76-91)
prefork.go (1)
  • IsChild (31-33)
color.go (1)
  • Colors (8-53)
hooks.go (3)
color.go (1)
  • Colors (8-53)
app.go (1)
  • Version (37-37)
prefork.go (1)
  • IsChild (31-33)
prefork_test.go (1)
app.go (1)
  • New (523-636)
⏰ 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). (4)
  • GitHub Check: unit (1.25.x, windows-latest)
  • GitHub Check: lint
  • GitHub Check: repeated
  • GitHub Check: Compare
🔇 Additional comments (11)
prefork.go (1)

95-123: Prefork child PID propagation into ListenData looks correct

Collecting childPIDs as []int and feeding them into prepareListenData/startupMessage integrates prefork metadata cleanly into the new startup flow. Cloning happens inside prepareListenData, so the master’s slice isn’t exposed, and hooks/startup message now have access to ChildPIDs without changing existing prefork behavior.

Also applies to: 139-146

hooks_test.go (4)

3-16: New imports are justified by added tests

The added os and runtime imports are used in metadata assertions and are scoped to tests, so there’s no production impact.


292-519: ListenData and PreStartupMessageData helper tests provide solid coverage

These tests thoroughly validate that prepareListenData populates host/port/TLS/version/app name/prefork/child PIDs/handler count/process count/PID/color scheme correctly and that the pre-startup helpers (EntryKeys, ResetEntries, AddInfo/AddWarning/AddError, DeleteEntry, and overwrite semantics) behave as intended. This gives good confidence in the new hook API surface.


645-655: executeOnListenHooks pointer change is correctly exercised

Switching executeOnListenHooks to accept *ListenData and passing &ListenData{...} in the test keeps the public handler signature (func(ListenData)) unchanged while ensuring handlers receive a value copy. The error assertion confirms propagation still works.


657-679: Pre/Post startup hook error paths are well covered

The new tests for executeOnPreStartupMessageHooks_Error and executeOnPostStartupMessageHooks_Error correctly verify that the first handler error is returned. This aligns the behavior with executeOnListenHooks and ensures callers can detect hook failures when they choose to.

listen.go (3)

165-175: Single ListenData pipeline for Listen/Listener is clean and consistent

Creating listenData once via prepareListenData, then feeding it through runOnListenHooks and printMessages for both Listen and Listener removes duplication and keeps all startup metadata (including TLS and prefork) in one place. Callers still see the same behavior, and config is passed by pointer throughout, so future fields will naturally propagate.

Also applies to: 208-223, 225-241


331-373: prepareListenData correctly normalizes address and isolates ChildPIDs

prepareListenData properly:

  • Normalizes host/port, defaulting to 0.0.0.0 or [::1] when the host is empty.
  • Derives ProcessCount from GOMAXPROCS only when prefork is enabled.
  • Clones childPIDs via slices.Clone, so internal ListenData.ChildPIDs can’t accidentally mutate the caller’s slice.
  • Populates handler count, PID, TLS, and prefork flags.

This is a good internal seam for both prefork and non-prefork flows.


375-467: startupMessage/printStartupEntries implement the new hookable startup banner correctly

The new startupMessage flow looks sound:

  • Builds sensible defaults (address, app name, handlers, prefork, PID, process count) into PreStartupMessageData.
  • Runs pre-startup hooks before rendering, allowing hooks to reset or override entries and the banner (BannerHeader) and to set PreventDefault.
  • Honors DisableStartupMessage, child-process detection (IsChild()), and PreventDefault to skip printing while still always executing post-startup hooks via defer.
  • Renders entries with a single printStartupEntries implementation that maps levels to labels/colors, and logs services and prefork child PIDs in a structured way.

The separation of concerns between data preparation, hooks, and rendering is clear and matches the proposed API.

Also applies to: 472-492

prefork_test.go (1)

78-100: Child-process “no startup message” test matches the new startup pipeline

Switching the test to call startupProcess(), then prepareListenData and startupMessage mirrors the actual prefork child flow while still asserting that nothing is printed when IsChild() is true. This keeps the regression check valid after the API refactor.

hooks.go (2)

9-51: New startup hook handler types and Hooks fields are consistent

Adding OnPreStartupMessageHandler/OnPostStartupMessageHandler and wiring them into Hooks alongside the existing hook types, with registration methods that lock h.app.mutex, fits the existing hook model and keeps handler signatures straightforward (pointers for pre/post startup data, value for ListenData).


222-236: Hook initialization and execute implementations follow existing patterns*

newHooks correctly initializes the new onPreStartup and onPostStartup slices, and the execute methods:

  • executeOnListenHooks(*ListenData) now take a pointer but still pass a value into handlers, preserving the public OnListenHandler API while avoiding accidental mutation.
  • executeOnPreStartupMessageHooks / executeOnPostStartupMessageHooks iterate handlers and return on the first error, matching how executeOnListenHooks behaves and enabling callers (like tests and startupMessage) to decide how to handle failures.

This is consistent with the rest of the hook subsystem.

Also applies to: 400-428

@ReneWerner87 ReneWerner87 merged commit e686668 into main Nov 19, 2025
16 of 17 checks passed
@ReneWerner87 ReneWerner87 deleted the add-startup-message-hooks-to-fiber-2 branch November 19, 2025 11:04
@github-project-automation github-project-automation bot moved this to Done in v3 Nov 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

📝 [Proposal]: Create API around app.startupMessage

4 participants