Skip to content
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

✨ feature: customizable colors #1977

Merged
merged 3 commits into from Aug 1, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
149 changes: 114 additions & 35 deletions app.go
Expand Up @@ -371,6 +371,11 @@ type Config struct {
// If set to true, will print all routes with their method, path and handler.
// Default: false
EnablePrintRoutes bool `json:"enable_print_routes"`

// You can define custom color scheme. They'll be used for startup message, route list and some middlewares.
//
// Optional. Default: DefaultColors
ColorScheme Colors `json:"color_scheme"`
}

// Static defines configuration options when defining static assets.
Expand Down Expand Up @@ -414,6 +419,54 @@ type Static struct {
Next func(c *Ctx) bool
}

// Colors is a struct to define custom colors for Fiber app and middlewares.
type Colors struct {
// Black color.
//
// Optional. Default: "\u001b[90m"
Black string

// Red color.
//
// Optional. Default: "\u001b[91m"
Red string

// Green color.
//
// Optional. Default: "\u001b[92m"
Green string

// Yellow color.
//
// Optional. Default: "\u001b[93m"
Yellow string

// Blue color.
//
// Optional. Default: "\u001b[94m"
Blue string

// Magenta color.
//
// Optional. Default: "\u001b[95m"
Magenta string

// Cyan color.
//
// Optional. Default: "\u001b[96m"
Cyan string

// White color.
//
// Optional. Default: "\u001b[97m"
White string

// Reset color.
//
// Optional. Default: "\u001b[0m"
Reset string
}

// RouteMessage is some message need to be print when server starts
type RouteMessage struct {
name string
Expand All @@ -431,6 +484,19 @@ const (
DefaultCompressedFileSuffix = ".fiber.gz"
)

// Default color codes
var DefaultColors = Colors{
Black: "\u001b[90m",
Red: "\u001b[91m",
Green: "\u001b[92m",
Yellow: "\u001b[93m",
Blue: "\u001b[94m",
Magenta: "\u001b[95m",
Cyan: "\u001b[96m",
White: "\u001b[97m",
Reset: "\u001b[0m",
}

// DefaultErrorHandler that process return errors from handlers
var DefaultErrorHandler = func(c *Ctx, err error) error {
code := StatusInternalServerError
Expand Down Expand Up @@ -523,6 +589,35 @@ func New(config ...Config) *App {
app.handleTrustedProxy(ipAddress)
}

// Override colors
if app.config.ColorScheme.Red == "" {
app.config.ColorScheme.Red = DefaultColors.Red
}

if app.config.ColorScheme.Green == "" {
app.config.ColorScheme.Green = DefaultColors.Green
}

if app.config.ColorScheme.Yellow == "" {
app.config.ColorScheme.Yellow = DefaultColors.Yellow
}

if app.config.ColorScheme.Blue == "" {
app.config.ColorScheme.Blue = DefaultColors.Blue
}

if app.config.ColorScheme.Magenta == "" {
app.config.ColorScheme.Magenta = DefaultColors.Magenta
}

if app.config.ColorScheme.Cyan == "" {
app.config.ColorScheme.Cyan = DefaultColors.Cyan
}

if app.config.ColorScheme.Reset == "" {
app.config.ColorScheme.Reset = DefaultColors.Reset
}

// Init appList
app.appList[""] = app

Expand Down Expand Up @@ -1145,17 +1240,8 @@ func (app *App) startupMessage(addr string, tls bool, pids string) {
return
}

const (
cBlack = "\u001b[90m"
// cRed = "\u001b[91m"
cCyan = "\u001b[96m"
// cGreen = "\u001b[92m"
// cYellow = "\u001b[93m"
// cBlue = "\u001b[94m"
// cMagenta = "\u001b[95m"
// cWhite = "\u001b[97m"
cReset = "\u001b[0m"
)
// Alias colors
colors := app.config.ColorScheme

value := func(s string, width int) string {
pad := width - len(s)
Expand All @@ -1166,7 +1252,7 @@ func (app *App) startupMessage(addr string, tls bool, pids string) {
if s == "Disabled" {
str += " " + s
} else {
str += fmt.Sprintf(" %s%s%s", cCyan, s, cBlack)
str += fmt.Sprintf(" %s%s%s", colors.Cyan, s, colors.Black)
}
return str
}
Expand All @@ -1185,7 +1271,7 @@ func (app *App) startupMessage(addr string, tls bool, pids string) {
centerValue := func(s string, width int) string {
pad := strconv.Itoa((width - len(s)) / 2)
str := fmt.Sprintf("%"+pad+"s", " ")
str += fmt.Sprintf("%s%s%s", cCyan, s, cBlack)
str += fmt.Sprintf("%s%s%s", colors.Cyan, s, colors.Black)
str += fmt.Sprintf("%"+pad+"s", " ")
if len(str)-10 < width {
str += " "
Expand Down Expand Up @@ -1226,7 +1312,7 @@ func (app *App) startupMessage(addr string, tls bool, pids string) {
procs = "1"
}

mainLogo := cBlack + " ┌───────────────────────────────────────────────────┐\n"
mainLogo := colors.Black + " ┌───────────────────────────────────────────────────┐\n"
if app.config.AppName != "" {
mainLogo += " │ " + centerValue(app.config.AppName, 49) + " │\n"
}
Expand All @@ -1246,7 +1332,7 @@ func (app *App) startupMessage(addr string, tls bool, pids string) {
" │ Handlers %s Processes %s │\n"+
" │ Prefork .%s PID ....%s │\n"+
" └───────────────────────────────────────────────────┘"+
cReset,
colors.Reset,
value(strconv.Itoa(int(app.handlersCount)), 14), value(procs, 12),
value(isPrefork, 14), value(strconv.Itoa(os.Getpid()), 14),
)
Expand Down Expand Up @@ -1277,9 +1363,9 @@ func (app *App) startupMessage(addr string, tls bool, pids string) {
lines = append(lines,
fmt.Sprintf(
newLine,
cBlack,
thisLine+cCyan+pad(strings.Join(itemsOnThisLine, ", "), 49-len(thisLine)),
cBlack,
colors.Black,
thisLine+colors.Cyan+pad(strings.Join(itemsOnThisLine, ", "), 49-len(thisLine)),
colors.Black,
),
)
}
Expand All @@ -1301,9 +1387,9 @@ func (app *App) startupMessage(addr string, tls bool, pids string) {

// Form logo
childPidsLogo = fmt.Sprintf(childPidsTemplate,
cBlack,
colors.Black,
strings.Join(lines, "\n")+"\n",
cReset,
colors.Reset,
)
}

Expand Down Expand Up @@ -1331,7 +1417,7 @@ func (app *App) startupMessage(addr string, tls bool, pids string) {
// Combine the two logos, line by line
output := "\n"
for i := range splitMainLogo {
output += cBlack + splitMainLogo[i] + " " + splitChildPidsLogo[i] + "\n"
output += colors.Black + splitMainLogo[i] + " " + splitChildPidsLogo[i] + "\n"
}

out := colorable.NewColorableStdout()
Expand All @@ -1353,17 +1439,9 @@ func (app *App) printRoutesMessage() {
return
}

const (
// cBlack = "\u001b[90m"
// cRed = "\u001b[91m"
cCyan = "\u001b[96m"
cGreen = "\u001b[92m"
cYellow = "\u001b[93m"
cBlue = "\u001b[94m"
// cMagenta = "\u001b[95m"
cWhite = "\u001b[97m"
// cReset = "\u001b[0m"
)
// Alias colors
colors := app.config.ColorScheme

var routes []RouteMessage
for _, routeStack := range app.stack {
for _, route := range routeStack {
Expand All @@ -1388,10 +1466,11 @@ func (app *App) printRoutesMessage() {
sort.Slice(routes, func(i, j int) bool {
return routes[i].path < routes[j].path
})
_, _ = fmt.Fprintf(w, "%smethod\t%s| %spath\t%s| %sname\t%s| %shandlers\n", cBlue, cWhite, cGreen, cWhite, cCyan, cWhite, cYellow)
_, _ = fmt.Fprintf(w, "%s------\t%s| %s----\t%s| %s----\t%s| %s--------\n", cBlue, cWhite, cGreen, cWhite, cCyan, cWhite, cYellow)

_, _ = fmt.Fprintf(w, "%smethod\t%s| %spath\t%s| %sname\t%s| %shandlers\n", colors.Blue, colors.White, colors.Green, colors.White, colors.Cyan, colors.White, colors.Yellow)
_, _ = fmt.Fprintf(w, "%s------\t%s| %s----\t%s| %s----\t%s| %s--------\n", colors.Blue, colors.White, colors.Green, colors.White, colors.Cyan, colors.White, colors.Yellow)
for _, route := range routes {
_, _ = fmt.Fprintf(w, "%s%s\t%s| %s%s\t%s| %s%s\t%s| %s%s\n", cBlue, route.method, cWhite, cGreen, route.path, cWhite, cCyan, route.name, cWhite, cYellow, route.handlers)
_, _ = fmt.Fprintf(w, "%s%s\t%s| %s%s\t%s| %s%s\t%s| %s%s\n", colors.Blue, route.method, colors.White, colors.Green, route.path, colors.White, colors.Cyan, route.name, colors.White, colors.Yellow, route.handlers)
}

_ = w.Flush()
Expand Down
44 changes: 17 additions & 27 deletions middleware/logger/logger.go
Expand Up @@ -61,19 +61,6 @@ const (
TagReset = "reset"
)

// Color values
const (
cBlack = "\u001b[90m"
cRed = "\u001b[91m"
cGreen = "\u001b[92m"
cYellow = "\u001b[93m"
cBlue = "\u001b[94m"
cMagenta = "\u001b[95m"
cCyan = "\u001b[96m"
cWhite = "\u001b[97m"
cReset = "\u001b[0m"
)

// New creates a new middleware handler
func New(config ...Config) fiber.Handler {
// Set default config
Expand Down Expand Up @@ -133,6 +120,9 @@ func New(config ...Config) fiber.Handler {
return c.Next()
}

// Alias colors
colors := c.App().Config().ColorScheme

// Set error handler once
once.Do(func() {
// get longested possible path
Expand Down Expand Up @@ -179,16 +169,16 @@ func New(config ...Config) fiber.Handler {
// Format error if exist
formatErr := ""
if chainErr != nil {
formatErr = cRed + " | " + chainErr.Error() + cReset
formatErr = colors.Red + " | " + chainErr.Error() + colors.Reset
}

// Format log to buffer
_, _ = buf.WriteString(fmt.Sprintf("%s |%s %3d %s| %7v | %15s |%s %-7s %s| %-"+errPaddingStr+"s %s\n",
timestamp.Load().(string),
statusColor(c.Response().StatusCode()), c.Response().StatusCode(), cReset,
statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset,
stop.Sub(start).Round(time.Millisecond),
c.IP(),
methodColor(c.Method()), c.Method(), cReset,
methodColor(c.Method(), colors), c.Method(), colors.Reset,
c.Path(),
formatErr,
))
Expand Down Expand Up @@ -240,7 +230,7 @@ func New(config ...Config) fiber.Handler {
return buf.WriteString(c.Route().Path)
case TagStatus:
if cfg.enableColors {
return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode()), c.Response().StatusCode(), cReset))
return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset))
}
return appendInt(buf, c.Response().StatusCode())
case TagResBody:
Expand All @@ -255,27 +245,27 @@ func New(config ...Config) fiber.Handler {
return buf.WriteString(c.Request().URI().QueryArgs().String())
case TagMethod:
if cfg.enableColors {
return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method()), c.Method(), cReset))
return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset))
}
return buf.WriteString(c.Method())
case TagBlack:
return buf.WriteString(cBlack)
return buf.WriteString(colors.Black)
case TagRed:
return buf.WriteString(cRed)
return buf.WriteString(colors.Red)
case TagGreen:
return buf.WriteString(cGreen)
return buf.WriteString(colors.Green)
case TagYellow:
return buf.WriteString(cYellow)
return buf.WriteString(colors.Yellow)
case TagBlue:
return buf.WriteString(cBlue)
return buf.WriteString(colors.Blue)
case TagMagenta:
return buf.WriteString(cMagenta)
return buf.WriteString(colors.Magenta)
case TagCyan:
return buf.WriteString(cCyan)
return buf.WriteString(colors.Cyan)
case TagWhite:
return buf.WriteString(cWhite)
return buf.WriteString(colors.White)
case TagReset:
return buf.WriteString(cReset)
return buf.WriteString(colors.Reset)
case TagError:
if chainErr != nil {
return buf.WriteString(chainErr.Error())
Expand Down
5 changes: 4 additions & 1 deletion middleware/logger/logger_test.go
Expand Up @@ -144,11 +144,14 @@ func Test_Logger_All(t *testing.T) {
Output: buf,
}))

// Alias colors
colors := app.Config().ColorScheme

resp, err := app.Test(httptest.NewRequest("GET", "/?foo=bar", nil))
utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode)

expected := fmt.Sprintf("%dHost=example.comhttp0.0.0.0example.com/?foo=bar/%s%s%s%s%s%s%s%s%sCannot GET /", os.Getpid(), cBlack, cRed, cGreen, cYellow, cBlue, cMagenta, cCyan, cWhite, cReset)
expected := fmt.Sprintf("%dHost=example.comhttp0.0.0.0example.com/?foo=bar/%s%s%s%s%s%s%s%s%sCannot GET /", os.Getpid(), colors.Black, colors.Red, colors.Green, colors.Yellow, colors.Blue, colors.Magenta, colors.Cyan, colors.White, colors.Reset)
utils.AssertEqual(t, expected, buf.String())
}

Expand Down