all: change send log to use x-www-form-urlencoded

This commit is contained in:
Cuong Manh Le
2025-01-14 14:31:37 +07:00
committed by Cuong Manh Le
parent 5a566c028a
commit a5c776c846
3 changed files with 54 additions and 37 deletions
+15 -9
View File
@@ -2,8 +2,9 @@ package cli
import ( import (
"context" "context"
"encoding/base64"
"encoding/json" "encoding/json"
"fmt"
"io"
"net" "net"
"net/http" "net/http"
"os" "os"
@@ -215,17 +216,23 @@ func (p *prog) registerControlServerHandler() {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
})) }))
p.cs.register(viewLogsPath, http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) { p.cs.register(viewLogsPath, http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) {
data, err := p.logContent() lr, err := p.logReader()
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
if len(data) == 0 { if lr.size == 0 {
w.WriteHeader(http.StatusMovedPermanently) w.WriteHeader(http.StatusMovedPermanently)
return return
} }
data, err := io.ReadAll(lr.r)
if err != nil {
http.Error(w, fmt.Sprintf("could not read log: %v", err), http.StatusInternalServerError)
return
}
if err := json.NewEncoder(w).Encode(&logViewResponse{Data: string(data)}); err != nil { if err := json.NewEncoder(w).Encode(&logViewResponse{Data: string(data)}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
http.Error(w, fmt.Sprintf("could not marshal log data: %v", err), http.StatusInternalServerError)
return return
} }
})) }))
@@ -234,22 +241,21 @@ func (p *prog) registerControlServerHandler() {
w.WriteHeader(http.StatusServiceUnavailable) w.WriteHeader(http.StatusServiceUnavailable)
return return
} }
data, err := p.logContent() r, err := p.logReader()
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
if len(data) == 0 { if r.size == 0 {
w.WriteHeader(http.StatusMovedPermanently) w.WriteHeader(http.StatusMovedPermanently)
return return
} }
logFile := base64.StdEncoding.EncodeToString(data)
req := &controld.LogsRequest{ req := &controld.LogsRequest{
UID: cdUID, UID: cdUID,
LogFile: logFile, Data: r.r,
} }
mainLog.Load().Debug().Msg("sending log file to ControlD server") mainLog.Load().Debug().Msg("sending log file to ControlD server")
resp := logSentResponse{Size: len(data)} resp := logSentResponse{Size: r.size}
if err := controld.SendLogs(req, cdDev); err != nil { if err := controld.SendLogs(req, cdDev); err != nil {
mainLog.Load().Error().Msgf("could not send log file to ControlD server: %v", err) mainLog.Load().Error().Msgf("could not send log file to ControlD server: %v", err)
resp.Error = err.Error() resp.Error = err.Error()
+29 -18
View File
@@ -3,6 +3,7 @@ package cli
import ( import (
"bytes" "bytes"
"errors" "errors"
"fmt"
"io" "io"
"os" "os"
"sync" "sync"
@@ -25,10 +26,15 @@ type logViewResponse struct {
} }
type logSentResponse struct { type logSentResponse struct {
Size int `json:"size"` Size int64 `json:"size"`
Error string `json:"error"` Error string `json:"error"`
} }
type logReader struct {
r io.ReadCloser
size int64
}
// logWriter is an internal buffer to keep track of runtime log when no logging is enabled. // logWriter is an internal buffer to keep track of runtime log when no logging is enabled.
type logWriter struct { type logWriter struct {
mu sync.Mutex mu sync.Mutex
@@ -111,8 +117,7 @@ func (p *prog) needInternalLogging() bool {
return true return true
} }
func (p *prog) logContent() ([]byte, error) { func (p *prog) logReader() (*logReader, error) {
var data []byte
if p.needInternalLogging() { if p.needInternalLogging() {
p.mu.Lock() p.mu.Lock()
lw := p.internalLogWriter lw := p.internalLogWriter
@@ -121,23 +126,29 @@ func (p *prog) logContent() ([]byte, error) {
return nil, errors.New("nil internal log writer") return nil, errors.New("nil internal log writer")
} }
lw.mu.Lock() lw.mu.Lock()
data = lw.buf.Bytes() lr := &logReader{r: io.NopCloser(bytes.NewReader(lw.buf.Bytes()))}
lr.size = int64(lw.buf.Len())
lw.mu.Unlock() lw.mu.Unlock()
if len(data) == 0 { if lr.size == 0 {
return nil, errors.New("internal log is empty") return nil, errors.New("internal log is empty")
} }
} else { return lr, nil
if p.cfg.Service.LogPath == "" {
return nil, nil
}
buf, err := os.ReadFile(normalizeLogFilePath(p.cfg.Service.LogPath))
if err != nil {
return nil, err
}
data = buf
if len(data) == 0 {
return nil, errors.New("log file is empty")
}
} }
return data, nil if p.cfg.Service.LogPath == "" {
return nil, nil
}
f, err := os.Open(normalizeLogFilePath(p.cfg.Service.LogPath))
if err != nil {
return nil, err
}
lr := &logReader{r: f}
if st, err := f.Stat(); err == nil {
lr.size = st.Size()
} else {
return nil, fmt.Errorf("f.Stat: %w", err)
}
if lr.size == 0 {
return nil, errors.New("log file is empty")
}
return lr, nil
} }
+10 -10
View File
@@ -77,8 +77,8 @@ type UtilityOrgRequest struct {
// LogsRequest contains request data for sending runtime logs to API. // LogsRequest contains request data for sending runtime logs to API.
type LogsRequest struct { type LogsRequest struct {
UID string `json:"uid"` UID string `json:"uid"`
LogFile string `json:"log_file"` Data io.ReadCloser `json:"-"`
} }
// FetchResolverConfig fetch Control D config for given uid. // FetchResolverConfig fetch Control D config for given uid.
@@ -160,20 +160,20 @@ func postUtilityAPI(version string, cdDev, lastUpdatedFailed bool, body io.Reade
} }
// SendLogs sends runtime log to ControlD API. // SendLogs sends runtime log to ControlD API.
func SendLogs(req *LogsRequest, cdDev bool) error { func SendLogs(lr *LogsRequest, cdDev bool) error {
body, _ := json.Marshal(req) defer lr.Data.Close()
return postLogAPI(cdDev, bytes.NewReader(body))
}
func postLogAPI(cdDev bool, body io.Reader) error {
apiUrl := logURLCom apiUrl := logURLCom
if cdDev { if cdDev {
apiUrl = logURLDev apiUrl = logURLDev
} }
req, err := http.NewRequest("POST", apiUrl, body) req, err := http.NewRequest("POST", apiUrl, lr.Data)
if err != nil { if err != nil {
return fmt.Errorf("http.NewRequest: %w", err) return fmt.Errorf("http.NewRequest: %w", err)
} }
q := req.URL.Query()
q.Set("uid", lr.UID)
req.URL.RawQuery = q.Encode()
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
transport := apiTransport(cdDev) transport := apiTransport(cdDev)
client := http.Client{ client := http.Client{
Timeout: 10 * time.Second, Timeout: 10 * time.Second,
@@ -181,7 +181,7 @@ func postLogAPI(cdDev bool, body io.Reader) error {
} }
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return fmt.Errorf("postLogAPI client.Do: %w", err) return fmt.Errorf("SendLogs client.Do: %w", err)
} }
defer resp.Body.Close() defer resp.Body.Close()
d := json.NewDecoder(resp.Body) d := json.NewDecoder(resp.Body)