feat: support clash premium's structured log stream (#735)

* feat: support clash premium's structured log stream

New version of Clash for Windows uses `ws://external-controller/logs?token=&level=info&format=structured` to get real time log. When Clash Premium Core reveices `format=structured`, it returns a different form of JSON log entry. Supporting this feature will allow better Clash for Windows integration

Signed-off-by: Misty <gyc990326@gmail.com>
This commit is contained in:
gVisor bot 2023-09-29 08:50:50 +08:00
parent 5cb57ffdc3
commit 0932a9c83f

View file

@ -293,6 +293,16 @@ type Log struct {
Type string `json:"type"` Type string `json:"type"`
Payload string `json:"payload"` Payload string `json:"payload"`
} }
type LogStructuredField struct {
Key string `json:"key"`
Value string `json:"value"`
}
type LogStructured struct {
Time string `json:"time"`
Level string `json:"level"`
Message string `json:"message"`
Fields []LogStructuredField `json:"fields"`
}
func getLogs(w http.ResponseWriter, r *http.Request) { func getLogs(w http.ResponseWriter, r *http.Request) {
levelText := r.URL.Query().Get("level") levelText := r.URL.Query().Get("level")
@ -300,6 +310,12 @@ func getLogs(w http.ResponseWriter, r *http.Request) {
levelText = "info" levelText = "info"
} }
formatText := r.URL.Query().Get("format")
isStructured := false
if formatText == "structured" {
isStructured = true
}
level, ok := log.LogLevelMapping[levelText] level, ok := log.LogLevelMapping[levelText]
if !ok { if !ok {
render.Status(r, http.StatusBadRequest) render.Status(r, http.StatusBadRequest)
@ -342,12 +358,27 @@ func getLogs(w http.ResponseWriter, r *http.Request) {
} }
buf.Reset() buf.Reset()
if !isStructured {
if err := json.NewEncoder(buf).Encode(Log{ if err := json.NewEncoder(buf).Encode(Log{
Type: logM.Type(), Type: logM.Type(),
Payload: logM.Payload, Payload: logM.Payload,
}); err != nil { }); err != nil {
break break
} }
} else {
newLevel := logM.Type()
if newLevel == "warning" {
newLevel = "warn"
}
if err := json.NewEncoder(buf).Encode(LogStructured{
Time: time.Now().Format(time.TimeOnly),
Level: newLevel,
Message: logM.Payload,
Fields: []LogStructuredField{},
}); err != nil {
break
}
}
var err error var err error
if wsConn == nil { if wsConn == nil {