Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
joy2fun committed Jan 6, 2024
0 parents commit 264e966
Show file tree
Hide file tree
Showing 7 changed files with 309 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/tmp
140 changes: 140 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# from https://github.com/golangci/golangci-lint/blob/master/.golangci.yml
linters-settings:
depguard:
list-type: blacklist
packages:
# logging is allowed only by logutils.Log, logrus
# is allowed to use only in logutils package
- github.com/sirupsen/logrus
packages-with-error-message:
- github.com/sirupsen/logrus: "logging is allowed only by logutils.Log"
dupl:
threshold: 100
exhaustive:
default-signifies-exhaustive: false
funlen:
lines: 100
statements: 50
gci:
local-prefixes: github.com/golangci/golangci-lint
goconst:
min-len: 2
min-occurrences: 2
gocritic:
enabled-tags:
- diagnostic
- experimental
- opinionated
- performance
- style
disabled-checks:
- dupImport # https://github.com/go-critic/go-critic/issues/845
- ifElseChain
- octalLiteral
- whyNoLint
- wrapperFunc
gocyclo:
min-complexity: 15
goimports:
local-prefixes: github.com/golangci/golangci-lint
golint:
min-confidence: 0
gomnd:
settings:
mnd:
# don't include the "operation" and "assign"
checks: argument,case,condition,return
govet:
check-shadowing: true
settings:
printf:
funcs:
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
lll:
line-length: 140
maligned:
suggest-new: true
misspell:
locale: US
nolintlint:
allow-leading-space: true # don't require machine-readable nolint directives (i.e. with no leading space)
allow-unused: false # report any unused nolint directives
require-explanation: false # don't require an explanation for nolint directives
require-specific: false # don't require nolint directives to be specific about which linter is being skipped

linters:
# please, do not use `enable-all`: it's deprecated and will be removed soon.
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
disable-all: true
enable:
- bodyclose
- deadcode
- depguard
- dogsled
- dupl
- errcheck
- exhaustive
- funlen
- gochecknoinits
- goconst
- gocritic
- gocyclo
- gofmt
- goimports
- golint
- gomnd
- goprintffuncname
- gosec
- gosimple
- govet
- ineffassign
- interfacer
- lll
- misspell
- nakedret
- noctx
- nolintlint
- rowserrcheck
- scopelint
- staticcheck
- structcheck
- stylecheck
- typecheck
- unconvert
- unparam
- unused
- varcheck
- whitespace

# don't enable:
# - asciicheck
# - gochecknoglobals
# - gocognit
# - godot
# - godox
# - goerr113
# - maligned
# - nestif
# - prealloc
# - testpackage
# - wsl

issues:
# Excluding configuration per-path, per-linter, per-text and per-source
exclude-rules:
- path: _test\.go
linters:
- gomnd

# https://github.com/go-critic/go-critic/issues/926
- linters:
- gocritic
text: "unnecessaryDefer:"

run:
skip-dirs:
- vendor
- hack
6 changes: 6 additions & 0 deletions .traefik.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
displayName: Request Logger
summary: Send request info to stdout.
type: middleware
import: github.com/joy2fun/traefik-plugin-log-request
testData:
responseBody: true
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Traefik plugin to log requests

file provider example:

```yml
http:
middlewares:
my-plugin:
plugin:
log-request:
responseBody: true # also including response body
```
crd example:
```yml
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: log-request
spec:
plugin:
log-request:
responseBody: true
```
helm chart values example:
```yaml
additionalArguments:
- >-
--experimental.localplugins.log-request.modulename=github.com/joy2fun/traefik-plugin-log-request
additionalVolumeMounts:
- mountPath: /plugins-local
name: plugins
deployment:
additionalVolumes:
- hostPath:
path: /data/plugins-local
name: plugins
```
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/joy2fun/traefik-plugin-log-request

go 1.14
Empty file added go.sum
Empty file.
118 changes: 118 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package traefik_plugin_log_request

import (
"bufio"
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"os"
)

// Config holds the plugin configuration.
type Config struct {
ResponseBody bool `json:"responseBody,omitempty"`
}

// CreateConfig creates and initializes the plugin configuration.
func CreateConfig() *Config {
return &Config{}
}

type logRequest struct {
name string
next http.Handler
responseBody bool
}

type RequestData struct {
URL string `json:"url"`
Host string `json:"host"`
Body string `json:"body"`
Headers string `json:"headers"`
ResponseBody string `json:"response_body"`
}

func New(_ context.Context, next http.Handler, config *Config, name string) (http.Handler, error) {
return &logRequest{
name: name,
next: next,
responseBody: config.ResponseBody,
}, nil
}

func (p *logRequest) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
body, err := io.ReadAll(req.Body)
if err != nil {
}

req.Body = io.NopCloser(bytes.NewBuffer(body))

wrappedWriter := &responseWriter{
ResponseWriter: rw,
}

p.next.ServeHTTP(wrappedWriter, req)

bodyBytes := wrappedWriter.buffer.Bytes()
rw.Write(bodyBytes)

headers := make(map[string]string)
for name, values := range req.Header {
headers[name] = values[0] // Take the first value of the header
}

jsonHeader, err := json.Marshal(headers)
if err != nil {
}

requestData := RequestData{
URL: req.URL.String(),
Host: req.Host,
Body: string(body),
Headers: string(jsonHeader),
}

if p.responseBody {
responseBody := io.NopCloser(bytes.NewBuffer(bodyBytes))
responseBodyBytes, err := io.ReadAll(responseBody)
if err != nil {
}

requestData.ResponseBody = string(responseBodyBytes)
}

jsonData, err := json.Marshal(requestData)
if err != nil {
}

os.Stdout.WriteString(string(jsonData) + "\n")
}

type responseWriter struct {
buffer bytes.Buffer

http.ResponseWriter
}

func (r *responseWriter) Write(p []byte) (int, error) {
return r.buffer.Write(p)
}

func (r *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
hijacker, ok := r.ResponseWriter.(http.Hijacker)
if !ok {
return nil, nil, fmt.Errorf("%T is not a http.Hijacker", r.ResponseWriter)
}

return hijacker.Hijack()
}

func (r *responseWriter) Flush() {
if flusher, ok := r.ResponseWriter.(http.Flusher); ok {
flusher.Flush()
}
}

0 comments on commit 264e966

Please sign in to comment.