Skip to content

Commit 817b2bf

Browse files
author
Giles Westwood
committed
stash a basic combined log format replayer
1 parent bcb42bb commit 817b2bf

12 files changed

Lines changed: 177 additions & 22 deletions

File tree

.github/workflows/release.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# This GitHub action can publish assets for release when a tag is created.
2+
# Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0).
3+
4+
name: release
5+
on:
6+
push:
7+
tags:
8+
- 'v*'
9+
workflow_dispatch:
10+
11+
jobs:
12+
goreleaser:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Checkout
16+
uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0
19+
20+
- name: Set up Go
21+
uses: actions/setup-go@v4
22+
with:
23+
go-version: ^1.17
24+
25+
- name: Run GoReleaser
26+
uses: goreleaser/goreleaser-action@v5
27+
with:
28+
version: latest
29+
args: release --clean
30+
env:
31+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/test.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: test
2+
on: [push, pull_request]
3+
jobs:
4+
build:
5+
name: Run Tests
6+
runs-on: ubuntu-latest
7+
steps:
8+
- name: Set up Go
9+
uses: actions/setup-go@v2
10+
with:
11+
go-version: ^1.17
12+
- name: Check out code into the Go module directory
13+
uses: actions/checkout@v2
14+
- name: Make test.sh executable
15+
run: chmod +x test.sh
16+
- name: Run test.sh
17+
run: ./test.sh
18+

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
access-log-replayer

.tool-versions

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
# golang 1.23.2
2-
golang 1.20.2
1+
golang 1.23.2

access-log-replayer.go

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@ package main
22

33
import (
44
"bufio"
5+
"context"
56
"flag"
67
"fmt"
78
"net/http"
89
"os"
9-
"strings"
10+
11+
accesslog "github.com/nekrassov01/access-log-parser"
1012
)
1113

1214
func main() {
13-
inputFile := flag.String("input-file", "", "Path to ELF log file")
15+
inputFile := flag.String("input-file", "", "Path to CLF log file")
1416
httpHost := flag.String("http_host", "", "Target HTTP host, e.g. localhost:8983")
1517
flag.Parse()
1618

@@ -26,32 +28,43 @@ func main() {
2628
}
2729
defer file.Close()
2830

29-
scanner := bufio.NewScanner(file)
30-
for scanner.Scan() {
31-
line := scanner.Text()
32-
if strings.HasPrefix(line, "#") || line == "" {
33-
continue // skip comments and empty lines
31+
handler := func(labels, values []string, isFirst bool) (string, error) {
32+
var method, path string
33+
for i, label := range labels {
34+
if label == "method" {
35+
method = values[i]
36+
}
37+
if label == "request_uri" {
38+
path = values[i]
39+
}
3440
}
35-
36-
fields := strings.Fields(line)
37-
if len(fields) < 5 {
38-
continue // skip malformed lines
39-
}
40-
41-
method := fields[3]
42-
url := fields[4]
43-
if method != "GET" {
44-
continue // only replay GET requests
41+
fmt.Printf("DEBUG: method='%s', path='%s'\n", method, path)
42+
if method != "GET" || path == "" {
43+
fmt.Println("DEBUG: Not a GET request or missing path, skipping")
44+
return "", nil
4545
}
46-
47-
fullURL := "http://" + *httpHost + url
46+
fullURL := "http://" + *httpHost + path
47+
fmt.Printf("DEBUG: Sending GET to %s\n", fullURL)
4848
resp, err := http.Get(fullURL)
4949
if err != nil {
5050
fmt.Printf("Request to %s failed: %v\n", fullURL, err)
51-
continue
51+
return "", nil
5252
}
5353
fmt.Printf("%s -> %d\n", fullURL, resp.StatusCode)
5454
resp.Body.Close()
55+
return "", nil
56+
}
57+
58+
opt := accesslog.Option{LineHandler: handler}
59+
parser := accesslog.NewApacheCLFRegexParser(context.Background(), os.Stdout, opt)
60+
scanner := bufio.NewScanner(file)
61+
for scanner.Scan() {
62+
line := scanner.Text()
63+
if line == "" {
64+
continue // skip empty lines
65+
}
66+
// ParseString will invoke the handler for each line
67+
_, _ = parser.ParseString(line)
5568
}
5669

5770
if err := scanner.Err(); err != nil {

custom_http.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from http.server import SimpleHTTPRequestHandler, HTTPServer
2+
3+
class LoggingHandler(SimpleHTTPRequestHandler):
4+
def log_message(self, format, *args):
5+
with open("server.log", "a") as f:
6+
f.write("%s - - [%s] %s\n" %
7+
(self.client_address[0],
8+
self.log_date_time_string(),
9+
format % args))
10+
11+
if __name__ == "__main__":
12+
server = HTTPServer(("localhost", 8983), LoggingHandler)
13+
print("Serving on port 8983...")
14+
server.serve_forever()

go.mod

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module access-log-replayer
2+
3+
go 1.21
4+
5+
require github.com/nekrassov01/access-log-parser v0.0.21
6+
7+
require (
8+
github.com/mattn/go-isatty v0.0.20 // indirect
9+
github.com/mattn/go-runewidth v0.0.15 // indirect
10+
github.com/nekrassov01/mintab v0.0.43 // indirect
11+
github.com/rivo/uniseg v0.2.0 // indirect
12+
golang.org/x/sys v0.6.0 // indirect
13+
)

go.sum

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
2+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
3+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
4+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
5+
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
6+
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
7+
github.com/nekrassov01/access-log-parser v0.0.21 h1:LkFczjiPCzuUdXqZe9K1o6DlO3bTxQD6je+v/339okE=
8+
github.com/nekrassov01/access-log-parser v0.0.21/go.mod h1:5HPmBBKK15FAGCE4bCv7Iei2/1h6Qrtgg+kc76Zs/eA=
9+
github.com/nekrassov01/mintab v0.0.43 h1:Wf6P+kKWpobaHk939buw4AjutsqSxT32ALOUI6LsT7o=
10+
github.com/nekrassov01/mintab v0.0.43/go.mod h1:mOBS91PE4x9II3jjtAB30WMCcTGB7xkHv1fq+WYdUdg=
11+
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
12+
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
13+
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
14+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

server.log

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
127.0.0.1 - - [02/Oct/2025 15:07:34] "GET / HTTP/1.1" 200 -
2+
127.0.0.1 - - [02/Oct/2025 15:07:34] code 404, message File not found
3+
127.0.0.1 - - [02/Oct/2025 15:07:34] "GET /solr/org/select?q=%28wibble&wt=xml&version=2.2 HTTP/1.1" 404 -

test.log

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
127.0.0.1 - - [02/Oct/2025:12:50:11 +0000] "GET / HTTP/1.1" 200 123 "-" "curl/7.68.0"
2+
10.99.153.11 - - [02/Oct/2025:12:50:11 +0000] "GET /solr/org/select?q=%28wibble&wt=xml&version=2.2 HTTP/1.1" 200 2641 "-" "Solr[org.apache.solr.client.solrj.impl.HttpSolrClient] 1.0"

0 commit comments

Comments
 (0)