-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Daisuke Maki
committed
Oct 12, 2024
0 parents
commit 164600e
Showing
14 changed files
with
534 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# To get started with Dependabot version updates, you'll need to specify which | ||
# package ecosystems to update and where the package manifests are located. | ||
# Please see the documentation for all configuration options: | ||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates | ||
|
||
version: 2 | ||
updates: | ||
- package-ecosystem: "gomod" # See documentation for possible values | ||
directory: "/" # Location of package manifests | ||
schedule: | ||
interval: "daily" | ||
target-branch: "v3" | ||
labels: | ||
- "go" | ||
- "dependencies" | ||
- "dependabot" | ||
- package-ecosystem: "github-actions" | ||
directory: "/" | ||
schedule: | ||
interval: "daily" | ||
target-branch: "v3" | ||
|
||
- package-ecosystem: "gomod" # See documentation for possible values | ||
directory: "/" # Location of package manifests | ||
schedule: | ||
interval: "daily" | ||
target-branch: "v2" | ||
labels: | ||
- "go" | ||
- "dependencies" | ||
- "dependabot" | ||
- package-ecosystem: "github-actions" | ||
directory: "/" | ||
schedule: | ||
interval: "daily" | ||
target-branch: "v2" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
name: Auto-Doc | ||
on: | ||
pull_request: | ||
branches: | ||
- v3 | ||
types: | ||
- closed | ||
workflow_dispatch: {} | ||
|
||
jobs: | ||
autodoc: | ||
runs-on: ubuntu-latest | ||
name: "Run commands to generate documentation" | ||
if: github.event.pull_request.merged == true | ||
steps: | ||
- name: Checkout repositor | ||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 | ||
- name: Process markdown files | ||
run: | | ||
find . -name '*.md' | xargs perl tools/autodoc.pl | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
name: CI | ||
on: [push, pull_request] | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
go: [ '1.23', '1.22' ] | ||
name: Go ${{ matrix.go }} test | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 | ||
- name: Check documentation generator | ||
run: | | ||
find . -name '*.md' | xargs env AUTODOC_DRYRUN=1 perl tools/autodoc.pl | ||
- name: Install Go stable version | ||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 | ||
with: | ||
go-version: ${{ matrix.go }} | ||
- name: Test | ||
run: go test -v -race | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
name: lint | ||
on: [push] | ||
jobs: | ||
golangci: | ||
name: lint | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 | ||
- name: Install Go stable version | ||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 | ||
with: | ||
go-version-file: go.mod | ||
- uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1 | ||
- name: Run go vet | ||
run: | | ||
go vet ./... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Binaries for programs and plugins | ||
*.exe | ||
*.exe~ | ||
*.dll | ||
*.so | ||
*.dylib | ||
|
||
# Test binary, built with `go test -c` | ||
*.test | ||
|
||
# Output of the go coverage tool, specifically when used with LiteIDE | ||
*.out | ||
|
||
# Dependency directories (remove the comment below to include it) | ||
# vendor/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2022 lestrrat | ||
|
||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# github.com/lestrrat-go/accesslog ![](https://github.com/lestrrat-go/accesslog/workflows/CI/badge.svg) [![Go Reference](https://pkg.go.dev/badge/github.com/lestrrat-go/accesslog.svg)](https://pkg.go.dev/github.com/lestrrat-go/accesslog) | ||
|
||
`github.com/lestrrat-go/accesslog` provides an HTTP middleware that logs accesslogs based on `log/slog`. | ||
|
||
# SYNOPSIS | ||
|
||
<!-- INCLUDE(accesslog_example_test.go) --> | ||
<!-- END INCLUDE --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package accesslog | ||
|
||
import ( | ||
"log/slog" | ||
"net/http" | ||
) | ||
|
||
// Middleware is the main object that you interact with. Despite its name | ||
// it's actually a Builder object, which needs to be applied to a http.Handler | ||
// by calling `Wrap()` | ||
// | ||
// For frameworks such as CHI that require a function that returns a http.Handler, | ||
// pass it the reference to the `Wrap()` method. | ||
type Middleware struct { | ||
clock Clock | ||
collector Collector | ||
logger *slog.Logger | ||
logLevel slog.Level | ||
recordResponse bool | ||
rwbuilder ResponseWriterBuilder | ||
} | ||
|
||
// Collector is an object that collects attributes from the request and response | ||
// and returns them as a slice of slog.Attr objects to be logged. | ||
// | ||
// If you do not like the default collector that is provided by the library, | ||
// you can always implement your own. If you just want to extend the standard | ||
// collector, you can embed the `Standard()` collector to your collector, and | ||
// add your own attributes to the return values | ||
type Collector interface { | ||
Collect(ResponseWriter, *http.Request) []slog.Attr | ||
} | ||
|
||
type handler struct { | ||
next http.Handler | ||
clock Clock | ||
collector Collector | ||
logger *slog.Logger | ||
logLevel slog.Level | ||
recordResponse bool | ||
rwbuilder ResponseWriterBuilder | ||
} | ||
|
||
// New creates a new Middleware object. You can further customize the object | ||
// by calling its methods. | ||
// | ||
// By default the `Standard()` collector is added to the list of collectors, | ||
// and the log level is set to `slog.LevelInfo` | ||
func New() *Middleware { | ||
return &Middleware{ | ||
clock: SystemClock{}, | ||
collector: Standard(), | ||
logLevel: slog.LevelInfo, | ||
logger: slog.Default(), | ||
rwbuilder: DefaultResponseWriterBuilder(), | ||
} | ||
} | ||
|
||
// Clock sets the Clock object to be used by the Middleware object. By default | ||
// `SystemClock` is used. | ||
func (al *Middleware) Clock(clock Clock) *Middleware { | ||
al.clock = clock | ||
return al | ||
} | ||
|
||
// Collector sets the Collector object to be used by the Middleware object. By | ||
// default `Standard()` is used. | ||
func (al *Middleware) Collector(collector Collector) *Middleware { | ||
al.collector = collector | ||
return al | ||
} | ||
|
||
// Logger sets the slog.Logger object to be used by the Middleware object. By | ||
// default `slog.Default()` is used. | ||
func (al *Middleware) Logger(logger *slog.Logger) *Middleware { | ||
al.logger = logger | ||
return al | ||
} | ||
|
||
// LogLevel sets the log level to be used by the Middleware object. By default | ||
// `slog.LevelInfo` is used. | ||
func (al *Middleware) LogLevel(level slog.Level) *Middleware { | ||
al.logLevel = level | ||
return al | ||
} | ||
|
||
// RecordResponse sets whether the response body should be recorded. By default | ||
// this is set to false for performance reasons. | ||
func (al *Middleware) RecordResponse(b bool) *Middleware { | ||
al.recordResponse = b | ||
return al | ||
} | ||
|
||
// Wrap returns a http.Handler that wraps the provided `next“ http.Handler object. | ||
func (al *Middleware) Wrap(next http.Handler) http.Handler { | ||
return handler{ | ||
next: next, | ||
clock: al.clock, | ||
collector: al.collector, | ||
logger: al.logger, | ||
logLevel: al.logLevel, | ||
recordResponse: al.recordResponse, | ||
rwbuilder: al.rwbuilder, | ||
} | ||
} | ||
|
||
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
rw := h.rwbuilder.Wrap(w, r, h.recordResponse) | ||
h.next.ServeHTTP(rw, r) | ||
rw.End() | ||
h.process(rw, r) | ||
} | ||
|
||
func (m handler) process(rw ResponseWriter, r *http.Request) { | ||
attrs := m.collector.Collect(rw, r) | ||
m.logger.LogAttrs(r.Context(), m.logLevel, "access", attrs...) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package accesslog_test | ||
|
||
import ( | ||
"fmt" | ||
"log/slog" | ||
"net/http" | ||
"net/http/httptest" | ||
"os" | ||
"time" | ||
|
||
"github.com/lestrrat-go/accesslog" | ||
) | ||
|
||
func Example() { | ||
al := accesslog.New(). | ||
// Set the clock to a static time to force duration=0 for testing | ||
Clock(accesslog.StaticClock(time.Time{})). | ||
Logger( | ||
slog.New( | ||
slog.NewJSONHandler(os.Stdout, | ||
&slog.HandlerOptions{ | ||
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { | ||
switch a.Key { | ||
case slog.TimeKey: | ||
// replace time to get static output for testing | ||
return slog.Time(slog.TimeKey, time.Time{}) | ||
case "remote_addr": | ||
// replace value to get static output for testing | ||
return slog.String("remote_addr", "127.0.0.1:99999") | ||
} | ||
return a | ||
}, | ||
}, | ||
), | ||
), | ||
) | ||
|
||
srv := httptest.NewServer(al.Wrap(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
w.WriteHeader(http.StatusOK) | ||
_, _ = w.Write([]byte("Hello, World!")) | ||
}))) | ||
defer srv.Close() | ||
|
||
_, err := http.Get(srv.URL) | ||
if err != nil { | ||
fmt.Println(err.Error()) | ||
return | ||
} | ||
|
||
// OUTPUT: | ||
// {"time":"0001-01-01T00:00:00Z","level":"INFO","msg":"access","remote_addr":"127.0.0.1:99999","http_method":"GET","path":"/","status":200,"body_bytes_sent":13,"http_referer":"","http_user_agent":"Go-http-client/1.1"} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package accesslog | ||
|
||
import "time" | ||
|
||
type Clock interface { | ||
Now() time.Time | ||
} | ||
|
||
// StaticClock is a Clock that always returns the same time. It's only used for | ||
// testing. | ||
type StaticClock time.Time | ||
|
||
func (c StaticClock) Now() time.Time { | ||
return time.Time(c) | ||
} | ||
|
||
// SystemClock is a wrapper around time.Now(). | ||
type SystemClock struct{} | ||
|
||
func (SystemClock) Now() time.Time { | ||
return time.Now() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module github.com/lestrrat-go/accesslog | ||
|
||
go 1.22.6 |
Oops, something went wrong.