Compare commits

..

5 Commits

Author SHA1 Message Date
3cbea14e84 fmt 2025-08-02 00:00:43 +03:00
6e59af1662 add json_format option for structure logging 2025-08-02 00:00:35 +03:00
8684d178e0 update Get example 2025-08-01 23:54:44 +03:00
945ab6c9cf update annotations 2025-08-01 23:53:40 +03:00
520901c331 update script error handling 2025-08-01 23:53:30 +03:00
9 changed files with 55 additions and 23 deletions

View File

@@ -1,26 +1,29 @@
local session = require("session")
local net = require("net")
local reqAddr local reqAddr
local logReq = true local logReq = true
if In.Params and In.Params.url then if session.request.params and session.request.params.url then
reqAddr = In.Params.url reqAddr = session.request.params.url
else else
Out.Error = { session.response.error = {
code = -32602, code = -32602,
message = "no url provided" message = "no url provided"
} }
return return
end end
local resp = Net.Http.Get(logReq, reqAddr) local resp = net.http.get_request(logReq, reqAddr)
if resp then if resp then
Out.Result.answer = { session.response.result.answer = {
status = resp.status, status = resp.status,
body = resp.body body = resp.body
} }
return return
end end
Out.Result.answer = { session.response.error = {
status = resp.status data = "error while requesting"
} }

View File

@@ -9,7 +9,7 @@
---@field request.params AnyTable Request parameters ---@field request.params AnyTable Request parameters
---@field response AnyTable Output context (write results/errors) ---@field response AnyTable Output context (write results/errors)
---@field response.result Any|string? Result payload (table or primitive) ---@field response.result Any|string? Result payload (table or primitive)
---@field response.error { code: integer, message: string }? Optional error info ---@field response.error { code: integer, message: string, data: any }? Optional error info
--- Global log module interface --- Global log module interface
---@class LogModule ---@class LogModule

View File

@@ -58,6 +58,7 @@ func (c *Compositor) LoadConf(path string) error {
v.SetDefault("updates.enabled", false) v.SetDefault("updates.enabled", false)
v.SetDefault("updates.check_interval", "2h") v.SetDefault("updates.check_interval", "2h")
v.SetDefault("updates.wanted_version", "latest-stable") v.SetDefault("updates.wanted_version", "latest-stable")
v.SetDefault("log.json_format", "false")
v.SetDefault("log.level", "info") v.SetDefault("log.level", "info")
v.SetDefault("log.output", "%stdout%") v.SetDefault("log.output", "%stdout%")

View File

@@ -55,6 +55,7 @@ type Updates struct {
} }
type Log struct { type Log struct {
JSON *bool `mapstructure:"json_format"`
Level *string `mapstructure:"level"` Level *string `mapstructure:"level"`
OutPath any `mapstructure:"output"` OutPath any `mapstructure:"output"`
} }

View File

@@ -90,6 +90,13 @@ func SetupLogger(o *config.Log) (*slog.Logger, error) {
writer = logFile writer = logFile
} }
log := slog.New(slog.NewJSONHandler(writer, &handlerOpts)) var handler slog.Handler
if *o.JSON {
handler = slog.NewJSONHandler(writer, &handlerOpts)
} else {
handler = slog.NewTextHandler(writer, &handlerOpts)
}
log := slog.New(handler)
return log, nil return log, nil
} }

View File

@@ -106,17 +106,17 @@ func (gs *GatewayServer) Handle(w http.ResponseWriter, r *http.Request) {
func (gs *GatewayServer) Route(ctx context.Context, sid string, r *http.Request, req *rpc.RPCRequest) (resp *rpc.RPCResponse) { func (gs *GatewayServer) Route(ctx context.Context, sid string, r *http.Request, req *rpc.RPCRequest) (resp *rpc.RPCResponse) {
defer utils.CatchPanicWithFallback(func(rec any) { defer utils.CatchPanicWithFallback(func(rec any) {
gs.x.SLog.Error("panic caught in handler", slog.Any("error", rec)) gs.x.SLog.Error("panic caught in handler", slog.Any("error", rec))
resp = rpc.NewError(rpc.ErrInternalError, "Internal server error (panic)", req.ID) resp = rpc.NewError(rpc.ErrInternalError, "Internal server error (panic)", nil, req.ID)
}) })
if req.JSONRPC != rpc.JSONRPCVersion { if req.JSONRPC != rpc.JSONRPCVersion {
gs.x.SLog.Info("invalid request received", slog.String("issue", rpc.ErrInvalidRequestS), slog.String("requested-version", req.JSONRPC)) gs.x.SLog.Info("invalid request received", slog.String("issue", rpc.ErrInvalidRequestS), slog.String("requested-version", req.JSONRPC))
return rpc.NewError(rpc.ErrInvalidRequest, rpc.ErrInvalidRequestS, req.ID) return rpc.NewError(rpc.ErrInvalidRequest, rpc.ErrInvalidRequestS, nil, req.ID)
} }
server, ok := gs.servers[serversApiVer(req.ContextVersion)] server, ok := gs.servers[serversApiVer(req.ContextVersion)]
if !ok { if !ok {
gs.x.SLog.Info("invalid request received", slog.String("issue", rpc.ErrContextVersionS), slog.String("requested-version", req.ContextVersion)) gs.x.SLog.Info("invalid request received", slog.String("issue", rpc.ErrContextVersionS), slog.String("requested-version", req.ContextVersion))
return rpc.NewError(rpc.ErrContextVersion, rpc.ErrContextVersionS, req.ID) return rpc.NewError(rpc.ErrContextVersion, rpc.ErrContextVersionS, nil, req.ID)
} }
// checks if request is notification // checks if request is notification

View File

@@ -2,7 +2,18 @@ package rpc
import "encoding/json" import "encoding/json"
func NewError(code int, message string, id *json.RawMessage) *RPCResponse { func NewError(code int, message string, data any, id *json.RawMessage) *RPCResponse {
if data != nil {
return &RPCResponse{
JSONRPC: JSONRPCVersion,
ID: id,
Error: map[string]any{
"code": code,
"message": message,
"data": data,
},
}
}
return &RPCResponse{ return &RPCResponse{
JSONRPC: JSONRPCVersion, JSONRPC: JSONRPCVersion,
ID: id, ID: id,

View File

@@ -11,17 +11,17 @@ import (
func (h *HandlerV1) Handle(_ context.Context, sid string, r *http.Request, req *rpc.RPCRequest) *rpc.RPCResponse { func (h *HandlerV1) Handle(_ context.Context, sid string, r *http.Request, req *rpc.RPCRequest) *rpc.RPCResponse {
if req.Method == "" { if req.Method == "" {
h.x.SLog.Info("invalid request received", slog.String("issue", rpc.ErrMethodNotFoundS), slog.String("requested-method", req.Method)) h.x.SLog.Info("invalid request received", slog.String("issue", rpc.ErrMethodNotFoundS), slog.String("requested-method", req.Method))
return rpc.NewError(rpc.ErrMethodIsMissing, rpc.ErrMethodIsMissingS, req.ID) return rpc.NewError(rpc.ErrMethodIsMissing, rpc.ErrMethodIsMissingS, nil, req.ID)
} }
method, err := h.resolveMethodPath(req.Method) method, err := h.resolveMethodPath(req.Method)
if err != nil { if err != nil {
if err.Error() == rpc.ErrInvalidMethodFormatS { if err.Error() == rpc.ErrInvalidMethodFormatS {
h.x.SLog.Info("invalid request received", slog.String("issue", rpc.ErrInvalidMethodFormatS), slog.String("requested-method", req.Method)) h.x.SLog.Info("invalid request received", slog.String("issue", rpc.ErrInvalidMethodFormatS), slog.String("requested-method", req.Method))
return rpc.NewError(rpc.ErrInvalidMethodFormat, rpc.ErrInvalidMethodFormatS, req.ID) return rpc.NewError(rpc.ErrInvalidMethodFormat, rpc.ErrInvalidMethodFormatS, nil, req.ID)
} else if err.Error() == rpc.ErrMethodNotFoundS { } else if err.Error() == rpc.ErrMethodNotFoundS {
h.x.SLog.Info("invalid request received", slog.String("issue", rpc.ErrMethodNotFoundS), slog.String("requested-method", req.Method)) h.x.SLog.Info("invalid request received", slog.String("issue", rpc.ErrMethodNotFoundS), slog.String("requested-method", req.Method))
return rpc.NewError(rpc.ErrMethodNotFound, rpc.ErrMethodNotFoundS, req.ID) return rpc.NewError(rpc.ErrMethodNotFound, rpc.ErrMethodNotFoundS, nil, req.ID)
} }
} }

View File

@@ -247,26 +247,26 @@ func (h *HandlerV1) handleLUA(sid string, r *http.Request, req *rpc.RPCRequest,
if _, err := os.Stat(prep); err == nil { if _, err := os.Stat(prep); err == nil {
if err := L.DoFile(prep); err != nil { if err := L.DoFile(prep); err != nil {
h.x.SLog.Error("script error", slog.String("script", path), slog.String("error", err.Error())) h.x.SLog.Error("script error", slog.String("script", path), slog.String("error", err.Error()))
return rpc.NewError(rpc.ErrInternalError, rpc.ErrInternalErrorS, req.ID) return rpc.NewError(rpc.ErrInternalError, rpc.ErrInternalErrorS, nil, req.ID)
} }
} }
if err := L.DoFile(path); err != nil { if err := L.DoFile(path); err != nil {
h.x.SLog.Error("script error", slog.String("script", path), slog.String("error", err.Error())) h.x.SLog.Error("script error", slog.String("script", path), slog.String("error", err.Error()))
return rpc.NewError(rpc.ErrInternalError, rpc.ErrInternalErrorS, req.ID) return rpc.NewError(rpc.ErrInternalError, rpc.ErrInternalErrorS, nil, req.ID)
} }
pkg := L.GetGlobal("package") pkg := L.GetGlobal("package")
pkgTbl, ok := pkg.(*lua.LTable) pkgTbl, ok := pkg.(*lua.LTable)
if !ok { if !ok {
h.x.SLog.Error("script error", slog.String("script", path), slog.String("error", "package not found")) h.x.SLog.Error("script error", slog.String("script", path), slog.String("error", "package not found"))
return rpc.NewError(rpc.ErrInternalError, rpc.ErrInternalErrorS, req.ID) return rpc.NewError(rpc.ErrInternalError, rpc.ErrInternalErrorS, nil, req.ID)
} }
loaded := pkgTbl.RawGetString("loaded") loaded := pkgTbl.RawGetString("loaded")
loadedTbl, ok := loaded.(*lua.LTable) loadedTbl, ok := loaded.(*lua.LTable)
if !ok { if !ok {
h.x.SLog.Error("script error", slog.String("script", path), slog.String("error", "package.loaded not found")) h.x.SLog.Error("script error", slog.String("script", path), slog.String("error", "package.loaded not found"))
return rpc.NewError(rpc.ErrInternalError, rpc.ErrInternalErrorS, req.ID) return rpc.NewError(rpc.ErrInternalError, rpc.ErrInternalErrorS, nil, req.ID)
} }
sessionVal := loadedTbl.RawGetString("session") sessionVal := loadedTbl.RawGetString("session")
@@ -288,23 +288,32 @@ func (h *HandlerV1) handleLUA(sid string, r *http.Request, req *rpc.RPCRequest,
outTbl, ok := outVal.(*lua.LTable) outTbl, ok := outVal.(*lua.LTable)
if !ok { if !ok {
h.x.SLog.Error("script error", slog.String("script", path), slog.String("error", "response is not a table")) h.x.SLog.Error("script error", slog.String("script", path), slog.String("error", "response is not a table"))
return rpc.NewError(rpc.ErrInternalError, rpc.ErrInternalErrorS, req.ID) return rpc.NewError(rpc.ErrInternalError, rpc.ErrInternalErrorS, nil, req.ID)
} }
if errVal := outTbl.RawGetString("error"); errVal != lua.LNil { if errVal := outTbl.RawGetString("error"); errVal != lua.LNil {
if errTbl, ok := errVal.(*lua.LTable); ok { if errTbl, ok := errVal.(*lua.LTable); ok {
code := rpc.ErrInternalError code := rpc.ErrInternalError
message := rpc.ErrInternalErrorS message := rpc.ErrInternalErrorS
data := make(map[string]any)
if c := errTbl.RawGetString("code"); c.Type() == lua.LTNumber { if c := errTbl.RawGetString("code"); c.Type() == lua.LTNumber {
code = int(c.(lua.LNumber)) code = int(c.(lua.LNumber))
} }
if msg := errTbl.RawGetString("message"); msg.Type() == lua.LTString { if msg := errTbl.RawGetString("message"); msg.Type() == lua.LTString {
message = msg.String() message = msg.String()
} }
rawData := errTbl.RawGetString("data")
if tbl, ok := rawData.(*lua.LTable); ok {
tbl.ForEach(func(k, v lua.LValue) { data[k.String()] = ConvertLuaTypesToGolang(v) })
} else {
h.x.SLog.Error("the script terminated with an error", slog.String("code", strconv.Itoa(code)), slog.String("message", message))
return rpc.NewError(code, message, rawData, req.ID)
}
h.x.SLog.Error("the script terminated with an error", slog.String("code", strconv.Itoa(code)), slog.String("message", message)) h.x.SLog.Error("the script terminated with an error", slog.String("code", strconv.Itoa(code)), slog.String("message", message))
return rpc.NewError(code, message, req.ID) return rpc.NewError(code, message, data, req.ID)
} }
return rpc.NewError(rpc.ErrInternalError, rpc.ErrInternalErrorS, req.ID) return rpc.NewError(rpc.ErrInternalError, rpc.ErrInternalErrorS, nil, req.ID)
} }
resultVal := outTbl.RawGetString("result") resultVal := outTbl.RawGetString("result")