Skip to content

Commit

Permalink
README, code cleanup, example
Browse files Browse the repository at this point in the history
  • Loading branch information
developer committed Apr 4, 2024
1 parent c4a6efb commit d8f5506
Show file tree
Hide file tree
Showing 8 changed files with 383 additions and 56 deletions.
82 changes: 82 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
linters-settings:
gosec:
confidence: low
godot:
exclude:
# Exclude swagger comments
- "@"
gci:
skip-generated: true
cyclop:
max-complexity: 15
tagliatelle:
# Check the struck tag name case.
case:
# Use the struct field name to check the name of the struct tag.
# Default: false
use-field-name: true
rules:
# Any struct tag type can be used.
# Support string case: `camel`, `pascal`, `kebab`, `snake`, `goCamel`, `goPascal`, `goKebab`, `goSnake`, `upper`, `lower`
json: snake
yaml: snake
xml: snake

linters:
enable:
- asasalint
- bidichk
- cyclop
- decorder
- dogsled
- dupl
- dupword
- errcheck
- errchkjson
- errorlint
- exhaustive
- forbidigo
- gocognit
- goconst
- gocritic
- gocyclo
- godot
- godox
- gofmt
- gofumpt
- goheader
- goimports
- gomnd
- gomoddirectives
- gomodguard
- gosimple
- gosec
- govet
- grouper
- importas
- ineffassign
- interfacebloat
- lll
- loggercheck
- maintidx
- makezero
- misspell
- nakedret
- nestif
- nilnil
- nlreturn
- nolintlint
- nonamedreturns
- prealloc
- predeclared
- promlinter
- reassign
- staticcheck
- stylecheck
- tagliatelle
- tenv
- typecheck
- usestdlibvars
- unparam
- unused
- whitespace
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 Ronald Bell.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
60 changes: 60 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# PrettyLogger for Echo

PrettyLogger is a middleware for the Echo web framework that enhances the visibility of server logs through color-coded and formatted output. It's designed to improve the readability of logs by highlighting different aspects of HTTP requests and responses such as status codes, method types, and data transfer sizes.

![PrettyLogger Screenshot](assets/screenshot.png)

## Features

- **Color-Coded Output**: Uses ANSI color codes to differentiate between HTTP methods, status codes, and other log components for quick visual parsing.
- **Formatted Path and Method Display**: Ensures consistent formatting for request paths and methods, including truncation with ellipses for long paths.
- **Readable Data Sizes**: Converts byte counts into a more readable format (b, Kb, Mb, Gb) for both incoming and outgoing data.
- **Performance Timing**: Logs the duration of each request in milliseconds, helping identify slow endpoints.

## Usage

To use PrettyLogger in your Echo project, follow these steps:

1. Import the PrettyLogger package into your Echo application.
2. Add the `Logger` function as middleware to your Echo instance.

Example:

```go
package main

import (
"github.com/labstack/echo/v4"
"github.com/rdbell/echo-pretty-logger"
)

func main() {
e := echo.New()

// Add PrettyLogger middleware
e.Use(prettyLogger.Logger)

// Define your routes
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})

// Start server
e.Start(":8080")
}
```

## Demo
To demo PrettyLogger's capabilities, navigate to the [`example`](/example) folder within this repository and run `go run main.go` in your terminal. This command will launch an Echo server demonstrating the middleware's color-coded and formatted logging output.

## Configuration

Currently, PrettyLogger does not require any configuration and works out of the box by providing sensible defaults for logging HTTP requests and responses. It automatically handles different HTTP methods, status codes, and content sizes in a visually distinct manner.

## Contribution

Contributions are welcome! If you'd like to improve PrettyLogger or suggest new features, feel free to fork the repository, make your changes, and submit a pull request.

# License

This project is licensed under the MIT License. See the [LICENSE](/LICENSE) file for more details.
Binary file added assets/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
151 changes: 151 additions & 0 deletions example/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package main

import (
"bytes"
"crypto/rand"
"fmt"
"io"
"log"
"net/http"

"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
prettylogger "github.com/rdbell/echo-pretty-logger"
)

const (
Kilobyte = 1024
Megabyte = 1024 * Kilobyte
)

func main() {
// Set up a new Echo server
e := echo.New()

// Hello World!
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})

// Redirect example
e.GET("/redirect", func(c echo.Context) error {
return c.Redirect(http.StatusMovedPermanently, "/")
})

// Unauthorized example
e.GET("/unauthorized", func(c echo.Context) error {
return c.String(http.StatusUnauthorized, "Unauthorized")
})

// POST example
e.POST("/post", func(c echo.Context) error {
// Return a 1MB response
bb := make([]byte, Megabyte)

return c.Blob(http.StatusOK, "application/octet-stream", bb)
})

// Start server
go func() {
err := e.Start(":8080")
if err != nil {
log.Fatalf("Failed to start server: %v", err)
}
}()

// Wait for server to start
for {
_, err := http.Get("http://localhost:8080/")
if err == nil {
break
}
}

// Conditional Logger Middleware
usePrettyLogger := false
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Condition to decide which logger to use
if usePrettyLogger {
return prettylogger.Logger(next)(c)
}

return middleware.Logger()(next)(c)
}
})

// Default logger
log.Println("\n\n\nBefore:")
testRequests()

// PettyLogger
log.Println("\n\n\nAfter:")
usePrettyLogger = true
testRequests()
}

func makeRequest(client *http.Client, method, url string) error {
var request *http.Request
var err error

// Handle different request methods
switch method {
case http.MethodPost:
// Generate a byte slice to use as the body of the POST request
data := make([]byte, Megabyte)
if _, err := rand.Read(data); err != nil {
return fmt.Errorf("failed to generate data for POST: %w", err)
}

// Use the byte slice as the body of the POST request
dataReader := bytes.NewReader(data)
request, err = http.NewRequest(http.MethodPost, url, dataReader)
if err != nil {
return fmt.Errorf("failed to create POST request: %w", err)
}
case http.MethodGet, http.MethodConnect:
request, err = http.NewRequest(method, url, nil)
if err != nil {
return fmt.Errorf("failed to create %s request: %w", method, err)
}
default:
return fmt.Errorf("unsupported method: %s", method)
}

// Send the request
response, err := client.Do(request)
if err != nil {
return fmt.Errorf("failed to send %s request: %w", method, err)
}
defer response.Body.Close()

// Read and discard the response body
_, err = io.Copy(io.Discard, response.Body)
if err != nil {
return fmt.Errorf("failed to read the response body: %w", err)
}

return nil
}

func testRequests() {
// Setup client
client := &http.Client{}

// List of URLs to test along with their request method
requests := map[string]string{
"http://localhost:8080/": http.MethodGet,
"http://localhost:8080/redirect": http.MethodGet,
"http://localhost:8080/unauthorized": http.MethodGet,
"http://localhost:8080/not_found": http.MethodConnect,
"http://localhost:8080/post": http.MethodPost,
}

// Iterate over the requests and make them
for url, method := range requests {
err := makeRequest(client, method, url)
if err != nil {
log.Fatalf("Failed to make request to %s: %v", url, err)
}
}
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.21.7
require github.com/labstack/echo/v4 v4.11.4

require (
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
Expand All @@ -14,4 +15,5 @@ require (
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8=
github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
Expand Down Expand Up @@ -27,5 +29,7 @@ golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading

0 comments on commit d8f5506

Please sign in to comment.