Skip to content

Commit bf39b27

Browse files
committed
use log to json
1 parent 8835f9a commit bf39b27

File tree

6 files changed

+139
-2
lines changed

6 files changed

+139
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
*.exe
22
cmd/simplehttpserver/simplehttpserver
3+
simplehttpserver

internal/runner/options.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type Options struct {
4040
Python bool
4141
CORS bool
4242
HTTPHeaders HTTPHeaders
43+
JSONLogFile string
4344
}
4445

4546
// ParseOptions parses the command line options for application
@@ -77,6 +78,7 @@ func ParseOptions() *Options {
7778
flagSet.BoolVar(&options.Python, "py", false, "Emulate Python Style"),
7879
flagSet.BoolVar(&options.CORS, "cors", false, "Enable Cross-Origin Resource Sharing (CORS)"),
7980
flagSet.Var(&options.HTTPHeaders, "header", "Add HTTP Response Header (name: value), can be used multiple times"),
81+
flagSet.StringVar(&options.JSONLogFile, "json-log", "", "JSON log file path for request logging"),
8082
)
8183

8284
flagSet.CreateGroup("debug", "Debug",

internal/runner/runner.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ func New(options *Options) (*Runner, error) {
7676
Python: r.options.Python,
7777
CORS: r.options.CORS,
7878
HTTPHeaders: r.options.HTTPHeaders,
79+
JSONLogFile: r.options.JSONLogFile,
7980
})
8081
if err != nil {
8182
return nil, err

pkg/httpserver/httpserver.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@ type Options struct {
3131
Python bool
3232
CORS bool
3333
HTTPHeaders []HTTPHeader
34+
JSONLogFile string
3435
}
3536

3637
// HTTPServer instance
3738
type HTTPServer struct {
38-
options *Options
39-
layers http.Handler
39+
options *Options
40+
layers http.Handler
41+
jsonLogger *JSONLogger
4042
}
4143

4244
// LayerHandler is the interface of all layer funcs
@@ -48,6 +50,15 @@ func New(options *Options) (*HTTPServer, error) {
4850
EnableUpload = options.EnableUpload
4951
EnableVerbose = options.Verbose
5052
EnableLogUA = options.LogUA
53+
54+
// Initialize JSON logger if specified
55+
if options.JSONLogFile != "" {
56+
jsonLogger, err := NewJSONLogger(options.JSONLogFile)
57+
if err != nil {
58+
return nil, err
59+
}
60+
h.jsonLogger = jsonLogger
61+
}
5162
folder, err := filepath.Abs(options.Folder)
5263
if err != nil {
5364
return nil, err
@@ -129,5 +140,8 @@ func (t *HTTPServer) ListenAndServeTLS() error {
129140

130141
// Close the service
131142
func (t *HTTPServer) Close() error {
143+
if t.jsonLogger != nil {
144+
return t.jsonLogger.Close()
145+
}
132146
return nil
133147
}

pkg/httpserver/jsonlogger.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package httpserver
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
"os"
8+
"sync"
9+
"time"
10+
)
11+
12+
// JSONLogEntry represents a single HTTP request log entry in JSON format
13+
type JSONLogEntry struct {
14+
Timestamp string `json:"timestamp"`
15+
RemoteAddr string `json:"remote_addr"`
16+
Method string `json:"method"`
17+
URL string `json:"url"`
18+
Proto string `json:"proto"`
19+
StatusCode int `json:"status_code"`
20+
Size int `json:"size"`
21+
UserAgent string `json:"user_agent,omitempty"`
22+
Headers map[string]string `json:"headers,omitempty"`
23+
RequestBody string `json:"request_body,omitempty"`
24+
ResponseBody string `json:"response_body,omitempty"`
25+
}
26+
27+
// JSONLogger handles writing JSON log entries to a file
28+
type JSONLogger struct {
29+
file *os.File
30+
mutex sync.Mutex
31+
enable bool
32+
}
33+
34+
// NewJSONLogger creates a new JSON logger instance
35+
func NewJSONLogger(filePath string) (*JSONLogger, error) {
36+
if filePath == "" {
37+
return &JSONLogger{enable: false}, nil
38+
}
39+
40+
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
41+
if err != nil {
42+
return nil, fmt.Errorf("failed to open JSON log file: %w", err)
43+
}
44+
45+
return &JSONLogger{
46+
file: file,
47+
enable: true,
48+
}, nil
49+
}
50+
51+
// LogRequest logs an HTTP request to the JSON file
52+
func (jl *JSONLogger) LogRequest(r *http.Request, statusCode, size int, userAgent string, headers map[string]string, requestBody, responseBody string) error {
53+
if !jl.enable {
54+
return nil
55+
}
56+
57+
entry := JSONLogEntry{
58+
Timestamp: time.Now().Format(time.RFC3339),
59+
RemoteAddr: r.RemoteAddr,
60+
Method: r.Method,
61+
URL: r.URL.String(),
62+
Proto: r.Proto,
63+
StatusCode: statusCode,
64+
Size: size,
65+
UserAgent: userAgent,
66+
Headers: headers,
67+
RequestBody: requestBody,
68+
ResponseBody: responseBody,
69+
}
70+
71+
jl.mutex.Lock()
72+
defer jl.mutex.Unlock()
73+
74+
jsonData, err := json.Marshal(entry)
75+
if err != nil {
76+
return fmt.Errorf("failed to marshal JSON log entry: %w", err)
77+
}
78+
79+
_, err = jl.file.Write(append(jsonData, '\n'))
80+
if err != nil {
81+
return fmt.Errorf("failed to write JSON log entry: %w", err)
82+
}
83+
84+
return nil
85+
}
86+
87+
// Close closes the JSON log file
88+
func (jl *JSONLogger) Close() error {
89+
if jl.file != nil {
90+
return jl.file.Close()
91+
}
92+
return nil
93+
}

pkg/httpserver/loglayer.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"net/http"
66
"net/http/httputil"
77
"time"
8+
89
"github.com/projectdiscovery/gologger"
910
)
1011

@@ -30,6 +31,31 @@ func (t *HTTPServer) loglayer(handler http.Handler) http.Handler {
3031
lrw := newLoggingResponseWriter(w, t.options.MaxDumpBodySize)
3132
handler.ServeHTTP(lrw, r)
3233

34+
// Log to JSON file if JSON logger is enabled
35+
if t.jsonLogger != nil {
36+
// Extract headers
37+
headers := make(map[string]string)
38+
for name, values := range r.Header {
39+
if len(values) > 0 {
40+
headers[name] = values[0]
41+
}
42+
}
43+
44+
// Extract request body from fullRequest
45+
requestBody := ""
46+
if len(fullRequest) > 0 {
47+
// Find the double CRLF that separates headers from body
48+
bodyStart := bytes.Index(fullRequest, []byte("\r\n\r\n"))
49+
if bodyStart != -1 && bodyStart+4 < len(fullRequest) {
50+
requestBody = string(fullRequest[bodyStart+4:])
51+
}
52+
}
53+
54+
// Log to JSON file
55+
_ = t.jsonLogger.LogRequest(r, lrw.statusCode, lrw.Size, r.UserAgent(), headers, requestBody, string(lrw.Data))
56+
}
57+
58+
// Continue with existing console logging
3359
if EnableVerbose {
3460
headers := new(bytes.Buffer)
3561
lrw.Header().Write(headers) //nolint

0 commit comments

Comments
 (0)