update log
This commit is contained in:
committed by
Haibo Chen(陈海博)
parent
b55a9d9a82
commit
5b4dbca9da
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@ -12,7 +12,7 @@
|
|||||||
"program": "${workspaceFolder}/objs/srs-sip.exe",
|
"program": "${workspaceFolder}/objs/srs-sip.exe",
|
||||||
"cwd": "${workspaceFolder}/objs",
|
"cwd": "${workspaceFolder}/objs",
|
||||||
"env": {},
|
"env": {},
|
||||||
"args": [],
|
"args": ["-c", "${workspaceFolder}/conf/config.yaml"],
|
||||||
"preLaunchTask": "build-windows",
|
"preLaunchTask": "build-windows",
|
||||||
"windows": {}
|
"windows": {}
|
||||||
},
|
},
|
||||||
@ -24,7 +24,7 @@
|
|||||||
"program": "${workspaceFolder}/objs/srs-sip",
|
"program": "${workspaceFolder}/objs/srs-sip",
|
||||||
"cwd": "${workspaceFolder}/objs",
|
"cwd": "${workspaceFolder}/objs",
|
||||||
"env": {},
|
"env": {},
|
||||||
"args": [],
|
"args": ["-c", "${workspaceFolder}/conf/config.yaml"],
|
||||||
"preLaunchTask": "build-linux",
|
"preLaunchTask": "build-linux",
|
||||||
"linux": {}
|
"linux": {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
# 通用配置
|
# 通用配置
|
||||||
common:
|
common:
|
||||||
log-level: "info"
|
log-level: "info"
|
||||||
|
log-file: "logs/srs-sip.log"
|
||||||
|
|
||||||
# GB28181配置
|
# GB28181配置
|
||||||
gb28181:
|
gb28181:
|
||||||
|
|||||||
31
main/main.go
31
main/main.go
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@ -14,10 +15,10 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/ossrs/go-oryx-lib/logger"
|
|
||||||
"github.com/ossrs/srs-sip/pkg/api"
|
"github.com/ossrs/srs-sip/pkg/api"
|
||||||
"github.com/ossrs/srs-sip/pkg/config"
|
"github.com/ossrs/srs-sip/pkg/config"
|
||||||
"github.com/ossrs/srs-sip/pkg/service"
|
"github.com/ossrs/srs-sip/pkg/service"
|
||||||
|
"github.com/ossrs/srs-sip/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func WaitTerminationSignal(cancel context.CancelFunc) {
|
func WaitTerminationSignal(cancel context.CancelFunc) {
|
||||||
@ -29,12 +30,11 @@ func WaitTerminationSignal(cancel context.CancelFunc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// 定义配置文件路径参数
|
|
||||||
configPath := flag.String("c", "", "配置文件路径")
|
configPath := flag.String("c", "", "配置文件路径")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if *configPath == "" {
|
if *configPath == "" {
|
||||||
logger.E(nil, "错误: 通过 -c 参数指定配置文件路径,比如:./srs-sip -c conf/config.yaml")
|
slog.Error("error: specify the config file path, like: ./srs-sip -c conf/config.yaml")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,18 +42,28 @@ func main() {
|
|||||||
|
|
||||||
conf, err := config.LoadConfig(*configPath)
|
conf, err := config.LoadConfig(*configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.E(nil, "load config failed: %v", err)
|
slog.Error("load config failed", "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := utils.SetupLogger(conf.Common.LogLevel, conf.Common.LogFile); err != nil {
|
||||||
|
slog.Error("setup logger failed", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("*****************************************************")
|
||||||
|
slog.Info(" ☆☆☆ 欢迎使用 SRS-SIP 服务 ☆☆☆")
|
||||||
|
slog.Info("*****************************************************")
|
||||||
|
slog.Info("srs-sip service starting", "config", *configPath, "log_file", conf.Common.LogFile)
|
||||||
|
|
||||||
sipSvr, err := service.NewService(ctx, conf)
|
sipSvr, err := service.NewService(ctx, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Ef("create service failed. err is %v", err.Error())
|
slog.Error("create service failed", "error", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := sipSvr.Start(); err != nil {
|
if err := sipSvr.Start(); err != nil {
|
||||||
logger.Ef("start sip service failed. err is %v", err.Error())
|
slog.Error("start sip service failed", "error", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +81,7 @@ func main() {
|
|||||||
// 先注册API路由
|
// 先注册API路由
|
||||||
apiSvr, err := api.NewHttpApiServer(conf, sipSvr)
|
apiSvr, err := api.NewHttpApiServer(conf, sipSvr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Ef("create http service failed. err is %v", err.Error())
|
slog.Error("create http service failed", "error", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
apiSvr.Start(router)
|
apiSvr.Start(router)
|
||||||
@ -80,6 +90,7 @@ func main() {
|
|||||||
router.PathPrefix("/").Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
router.PathPrefix("/").Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// 如果是API路径,直接返回404
|
// 如果是API路径,直接返回404
|
||||||
if strings.HasPrefix(r.URL.Path, "/srs-sip/v1/") {
|
if strings.HasPrefix(r.URL.Path, "/srs-sip/v1/") {
|
||||||
|
slog.Info("api path not found", "path", r.URL.Path)
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -89,6 +100,7 @@ func main() {
|
|||||||
_, err := os.Stat(filePath)
|
_, err := os.Stat(filePath)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
// 如果文件不存在,返回 index.html
|
// 如果文件不存在,返回 index.html
|
||||||
|
slog.Info("file not found, redirect to index", "path", r.URL.Path)
|
||||||
r.URL.Path = "/"
|
r.URL.Path = "/"
|
||||||
}
|
}
|
||||||
fs.ServeHTTP(w, r)
|
fs.ServeHTTP(w, r)
|
||||||
@ -106,13 +118,14 @@ func main() {
|
|||||||
IdleTimeout: 30 * time.Second,
|
IdleTimeout: 30 * time.Second,
|
||||||
ReadHeaderTimeout: 5 * time.Second,
|
ReadHeaderTimeout: 5 * time.Second,
|
||||||
}
|
}
|
||||||
logger.Tf(ctx, "http server listen on %s, home is %v", httpPort, conf.Http.Dir)
|
slog.Info("http server listen", "port", httpPort, "home", conf.Http.Dir)
|
||||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
logger.Ef(ctx, "listen on %s failed", httpPort)
|
slog.Error("listen failed", "port", httpPort, "error", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
WaitTerminationSignal(cancel)
|
WaitTerminationSignal(cancel)
|
||||||
|
|
||||||
sipSvr.Stop()
|
sipSvr.Stop()
|
||||||
|
slog.Info("srs-sip service stopped")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/ossrs/go-oryx-lib/logger"
|
|
||||||
"github.com/ossrs/srs-sip/pkg/config"
|
"github.com/ossrs/srs-sip/pkg/config"
|
||||||
"github.com/ossrs/srs-sip/pkg/service"
|
"github.com/ossrs/srs-sip/pkg/service"
|
||||||
)
|
)
|
||||||
@ -29,7 +28,7 @@ func (h *HttpApiServer) Start(router *mux.Router) {
|
|||||||
// 创建一个子路由,所有API都以/srs-sip/v1为前缀
|
// 创建一个子路由,所有API都以/srs-sip/v1为前缀
|
||||||
apiRouter := router.PathPrefix("/srs-sip/v1").Subrouter()
|
apiRouter := router.PathPrefix("/srs-sip/v1").Subrouter()
|
||||||
|
|
||||||
logger.Tf(context.Background(), "Registering API routes under /srs-sip/v1")
|
slog.Info("Registering API routes under /srs-sip/v1")
|
||||||
h.RegisterRoutes(apiRouter)
|
h.RegisterRoutes(apiRouter)
|
||||||
|
|
||||||
// 打印所有注册的路由,包含更详细的信息
|
// 打印所有注册的路由,包含更详细的信息
|
||||||
@ -38,8 +37,11 @@ func (h *HttpApiServer) Start(router *mux.Router) {
|
|||||||
pathRegexp, _ := route.GetPathRegexp()
|
pathRegexp, _ := route.GetPathRegexp()
|
||||||
methods, _ := route.GetMethods()
|
methods, _ := route.GetMethods()
|
||||||
queries, _ := route.GetQueriesTemplates()
|
queries, _ := route.GetQueriesTemplates()
|
||||||
logger.Tf(context.Background(), "Route Details: Path=%v, Regexp=%v, Methods=%v, Queries=%v",
|
slog.Debug("Route Details",
|
||||||
pathTemplate, pathRegexp, methods, queries)
|
"path", pathTemplate,
|
||||||
|
"regexp", pathRegexp,
|
||||||
|
"methods", methods,
|
||||||
|
"queries", queries)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,11 +5,11 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ossrs/go-oryx-lib/errors"
|
"github.com/ossrs/go-oryx-lib/errors"
|
||||||
"github.com/ossrs/go-oryx-lib/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type IMedia interface {
|
type IMedia interface {
|
||||||
@ -30,7 +30,7 @@ func apiRequest(ctx context.Context, r string, req interface{}, res interface{})
|
|||||||
return errors.Wrapf(err, "Marshal body %v", req)
|
return errors.Wrapf(err, "Marshal body %v", req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.Tf(ctx, "Request url api=%v with %v bytes", r, buf.Len())
|
slog.Debug("API request", "url", r, "size", buf.Len())
|
||||||
|
|
||||||
method := "POST"
|
method := "POST"
|
||||||
if req == nil {
|
if req == nil {
|
||||||
@ -56,7 +56,7 @@ func apiRequest(ctx context.Context, r string, req interface{}, res interface{})
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "Read response for %v", buf.String())
|
return errors.Wrapf(err, "Read response for %v", buf.String())
|
||||||
}
|
}
|
||||||
logger.Tf(ctx, "Response from %v is %v bytes", r, len(b2))
|
slog.Debug("API response", "url", r, "size", len(b2))
|
||||||
|
|
||||||
errorCode := struct {
|
errorCode := struct {
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
@ -71,7 +71,7 @@ func apiRequest(ctx context.Context, r string, req interface{}, res interface{})
|
|||||||
if err := json.Unmarshal(b2, res); err != nil {
|
if err := json.Unmarshal(b2, res); err != nil {
|
||||||
return errors.Wrapf(err, "Unmarshal %v", string(b2))
|
return errors.Wrapf(err, "Unmarshal %v", string(b2))
|
||||||
}
|
}
|
||||||
logger.Tf(ctx, "Parse response to code=%v ok, %v", errorCode.Code, res)
|
slog.Debug("Parse API response", "code", errorCode.Code, "response", res)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ossrs/go-oryx-lib/logger"
|
|
||||||
"github.com/ossrs/srs-sip/pkg/models"
|
"github.com/ossrs/srs-sip/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -98,7 +97,10 @@ func (dm *deviceManager) checkHeartbeats() {
|
|||||||
device.SourceAddr = ""
|
device.SourceAddr = ""
|
||||||
device.Online = false
|
device.Online = false
|
||||||
dm.devices.Store(key, device)
|
dm.devices.Store(key, device)
|
||||||
logger.Wf(context.Background(), "Device %s is offline due to heartbeat timeout, HeartBeatInterval: %v", device.DeviceID, device.HeartBeatInterval)
|
slog.Warn("Device is offline due to heartbeat timeout",
|
||||||
|
"device_id", device.DeviceID,
|
||||||
|
"heartbeat_interval", device.HeartBeatInterval,
|
||||||
|
"heartbeat_count", device.HeartBeatCount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|||||||
@ -3,12 +3,12 @@ package service
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/emiago/sipgo/sip"
|
"github.com/emiago/sipgo/sip"
|
||||||
"github.com/ossrs/go-oryx-lib/logger"
|
|
||||||
"github.com/ossrs/srs-sip/pkg/models"
|
"github.com/ossrs/srs-sip/pkg/models"
|
||||||
"github.com/ossrs/srs-sip/pkg/service/stack"
|
"github.com/ossrs/srs-sip/pkg/service/stack"
|
||||||
"golang.org/x/net/html/charset"
|
"golang.org/x/net/html/charset"
|
||||||
@ -31,7 +31,7 @@ func (s *UAS) isSameIP(addr1, addr2 string) bool {
|
|||||||
func (s *UAS) onRegister(req *sip.Request, tx sip.ServerTransaction) {
|
func (s *UAS) onRegister(req *sip.Request, tx sip.ServerTransaction) {
|
||||||
id := req.From().Address.User
|
id := req.From().Address.User
|
||||||
if len(id) != GB28181_ID_LENGTH {
|
if len(id) != GB28181_ID_LENGTH {
|
||||||
logger.E(s.ctx, "invalid device ID")
|
slog.Error("invalid device ID")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ func (s *UAS) onRegister(req *sip.Request, tx sip.ServerTransaction) {
|
|||||||
// Validate Authorization
|
// Validate Authorization
|
||||||
authInfo := ParseAuthorization(authHeader[0].Value())
|
authInfo := ParseAuthorization(authHeader[0].Value())
|
||||||
if !ValidateAuth(authInfo, s.conf.GB28181.Auth.Password) {
|
if !ValidateAuth(authInfo, s.conf.GB28181.Auth.Password) {
|
||||||
logger.Ef(s.ctx, "%s auth failed, source: %s", id, req.Source())
|
slog.Error("auth failed", "device_id", id, "source", req.Source())
|
||||||
s.respondRegister(req, http.StatusForbidden, "Auth Failed", tx)
|
s.respondRegister(req, http.StatusForbidden, "Auth Failed", tx)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -61,20 +61,20 @@ func (s *UAS) onRegister(req *sip.Request, tx sip.ServerTransaction) {
|
|||||||
exp := exps[0]
|
exp := exps[0]
|
||||||
expSec, err := strconv.ParseInt(exp.Value(), 10, 32)
|
expSec, err := strconv.ParseInt(exp.Value(), 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Ef(s.ctx, "parse expires header error: %s", err.Error())
|
slog.Error("parse expires header error", "error", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if expSec == 0 {
|
if expSec == 0 {
|
||||||
isUnregister = true
|
isUnregister = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.E(s.ctx, "empty expires header")
|
slog.Error("empty expires header")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if isUnregister {
|
if isUnregister {
|
||||||
DM.RemoveDevice(id)
|
DM.RemoveDevice(id)
|
||||||
logger.Wf(s.ctx, "Device %s unregistered", id)
|
slog.Warn("Device unregistered", "device_id", id)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
if d, ok := DM.GetDevice(id); !ok {
|
if d, ok := DM.GetDevice(id); !ok {
|
||||||
@ -84,13 +84,13 @@ func (s *UAS) onRegister(req *sip.Request, tx sip.ServerTransaction) {
|
|||||||
NetworkType: req.Transport(),
|
NetworkType: req.Transport(),
|
||||||
})
|
})
|
||||||
s.respondRegister(req, http.StatusOK, "OK", tx)
|
s.respondRegister(req, http.StatusOK, "OK", tx)
|
||||||
logger.Tf(s.ctx, "%s Register success, source:%s, req: %s", id, req.Source(), req.String())
|
slog.Info("Register success", "device_id", id, "source", req.Source(), "request", req.String())
|
||||||
|
|
||||||
go s.ConfigDownload(id)
|
go s.ConfigDownload(id)
|
||||||
go s.Catalog(id)
|
go s.Catalog(id)
|
||||||
} else {
|
} else {
|
||||||
if d.SourceAddr != "" && !s.isSameIP(d.SourceAddr, req.Source()) {
|
if d.SourceAddr != "" && !s.isSameIP(d.SourceAddr, req.Source()) {
|
||||||
logger.Ef(s.ctx, "Device %s[%s] already registered, please change another ID.", id, d.SourceAddr, req.Source())
|
slog.Error("Device already registered", "device_id", id, "old_source", d.SourceAddr, "new_source", req.Source())
|
||||||
// TODO: 如果ID重复,应采用虚拟ID
|
// TODO: 如果ID重复,应采用虚拟ID
|
||||||
s.respondRegister(req, http.StatusBadRequest, "Conflict Device ID", tx)
|
s.respondRegister(req, http.StatusBadRequest, "Conflict Device ID", tx)
|
||||||
} else {
|
} else {
|
||||||
@ -99,7 +99,7 @@ func (s *UAS) onRegister(req *sip.Request, tx sip.ServerTransaction) {
|
|||||||
DM.UpdateDevice(id, d)
|
DM.UpdateDevice(id, d)
|
||||||
s.respondRegister(req, http.StatusOK, "OK", tx)
|
s.respondRegister(req, http.StatusOK, "OK", tx)
|
||||||
|
|
||||||
logger.Tf(s.ctx, "%s Re-register success, source:%s, req: %s", id, req.Source(), req.String())
|
slog.Info("Re-register success", "device_id", id, "source", req.Source(), "request", req.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,21 +114,21 @@ func (s *UAS) respondRegister(req *sip.Request, code sip.StatusCode, reason stri
|
|||||||
func (s *UAS) onMessage(req *sip.Request, tx sip.ServerTransaction) {
|
func (s *UAS) onMessage(req *sip.Request, tx sip.ServerTransaction) {
|
||||||
id := req.From().Address.User
|
id := req.From().Address.User
|
||||||
if len(id) != 20 {
|
if len(id) != 20 {
|
||||||
logger.Ef(s.ctx, "invalid device ID %s", req.String())
|
slog.Error("invalid device ID", "request", req.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
//logger.Tf(s.ctx, "Received MESSAGE: %s", req.String())
|
slog.Debug("Received MESSAGE", "request", req.String())
|
||||||
|
|
||||||
temp := &models.XmlMessageInfo{}
|
temp := &models.XmlMessageInfo{}
|
||||||
decoder := xml.NewDecoder(bytes.NewReader([]byte(req.Body())))
|
decoder := xml.NewDecoder(bytes.NewReader([]byte(req.Body())))
|
||||||
decoder.CharsetReader = charset.NewReaderLabel
|
decoder.CharsetReader = charset.NewReaderLabel
|
||||||
if err := decoder.Decode(temp); err != nil {
|
if err := decoder.Decode(temp); err != nil {
|
||||||
logger.Ef(s.ctx, "decode message error: %s\n message:%s", err.Error(), req.Body())
|
slog.Error("decode message error", "error", err.Error(), "message", req.Body())
|
||||||
}
|
}
|
||||||
var body string
|
var body string
|
||||||
switch temp.CmdType {
|
switch temp.CmdType {
|
||||||
case "Keepalive":
|
case "Keepalive":
|
||||||
logger.T(s.ctx, "Keepalive")
|
slog.Debug("Keepalive")
|
||||||
if d, ok := DM.GetDevice(temp.DeviceID); ok && d.Online {
|
if d, ok := DM.GetDevice(temp.DeviceID); ok && d.Online {
|
||||||
// 更新设备心跳时间
|
// 更新设备心跳时间
|
||||||
DM.UpdateDeviceHeartbeat(temp.DeviceID)
|
DM.UpdateDeviceHeartbeat(temp.DeviceID)
|
||||||
@ -138,16 +138,16 @@ func (s *UAS) onMessage(req *sip.Request, tx sip.ServerTransaction) {
|
|||||||
}
|
}
|
||||||
case "SensorCatalog": // 兼容宇视,非国标
|
case "SensorCatalog": // 兼容宇视,非国标
|
||||||
case "Catalog":
|
case "Catalog":
|
||||||
logger.T(s.ctx, "Catalog")
|
slog.Debug("Catalog")
|
||||||
DM.UpdateChannels(temp.DeviceID, temp.DeviceList...)
|
DM.UpdateChannels(temp.DeviceID, temp.DeviceList...)
|
||||||
//go s.AutoInvite(temp.DeviceID, temp.DeviceList...)
|
//go s.AutoInvite(temp.DeviceID, temp.DeviceList...)
|
||||||
case "ConfigDownload":
|
case "ConfigDownload":
|
||||||
logger.T(s.ctx, "ConfigDownload")
|
slog.Debug("ConfigDownload")
|
||||||
DM.UpdateDeviceConfig(temp.DeviceID, &temp.BasicParam)
|
DM.UpdateDeviceConfig(temp.DeviceID, &temp.BasicParam)
|
||||||
case "Alarm":
|
case "Alarm":
|
||||||
logger.T(s.ctx, "Alarm")
|
slog.Debug("Alarm")
|
||||||
case "RecordInfo":
|
case "RecordInfo":
|
||||||
logger.T(s.ctx, "RecordInfo")
|
slog.Debug("RecordInfo")
|
||||||
// 从 recordQueryResults 中获取对应通道的结果通道
|
// 从 recordQueryResults 中获取对应通道的结果通道
|
||||||
if ch, ok := s.recordQueryResults.Load(temp.DeviceID); ok {
|
if ch, ok := s.recordQueryResults.Load(temp.DeviceID); ok {
|
||||||
// 发送查询结果
|
// 发送查询结果
|
||||||
@ -155,7 +155,7 @@ func (s *UAS) onMessage(req *sip.Request, tx sip.ServerTransaction) {
|
|||||||
resultChan <- temp
|
resultChan <- temp
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
logger.Wf(s.ctx, "Not supported CmdType: %s", temp.CmdType)
|
slog.Warn("Not supported CmdType", "cmd_type", temp.CmdType)
|
||||||
response := sip.NewResponseFromRequest(req, http.StatusBadRequest, "", nil)
|
response := sip.NewResponseFromRequest(req, http.StatusBadRequest, "", nil)
|
||||||
tx.Respond(response)
|
tx.Respond(response)
|
||||||
return
|
return
|
||||||
@ -164,6 +164,6 @@ func (s *UAS) onMessage(req *sip.Request, tx sip.ServerTransaction) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *UAS) onNotify(req *sip.Request, tx sip.ServerTransaction) {
|
func (s *UAS) onNotify(req *sip.Request, tx sip.ServerTransaction) {
|
||||||
logger.T(s.ctx, "Received NOTIFY request")
|
slog.Debug("Received NOTIFY request")
|
||||||
tx.Respond(sip.NewResponseFromRequest(req, http.StatusOK, "OK", nil))
|
tx.Respond(sip.NewResponseFromRequest(req, http.StatusOK, "OK", nil))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,12 +2,12 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/emiago/sipgo/sip"
|
"github.com/emiago/sipgo/sip"
|
||||||
"github.com/ossrs/go-oryx-lib/errors"
|
"github.com/ossrs/go-oryx-lib/errors"
|
||||||
"github.com/ossrs/go-oryx-lib/logger"
|
|
||||||
"github.com/ossrs/srs-sip/pkg/media"
|
"github.com/ossrs/srs-sip/pkg/media"
|
||||||
"github.com/ossrs/srs-sip/pkg/models"
|
"github.com/ossrs/srs-sip/pkg/models"
|
||||||
"github.com/ossrs/srs-sip/pkg/service/stack"
|
"github.com/ossrs/srs-sip/pkg/service/stack"
|
||||||
@ -110,7 +110,7 @@ Scale: %.1f
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *UAS) AddSession(key string, status Session) {
|
func (s *UAS) AddSession(key string, status Session) {
|
||||||
logger.Tf(s.ctx, "AddSession: %s, %+v", key, status)
|
slog.Info("AddSession", "key", key, "status", status)
|
||||||
s.Streams.Store(key, status)
|
s.Streams.Store(key, status)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,7 +314,7 @@ func (s *UAS) Bye(req models.ByeRequest) error {
|
|||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := s.media.Unpublish(session.Ssrc); err != nil {
|
if err := s.media.Unpublish(session.Ssrc); err != nil {
|
||||||
logger.Ef(s.ctx, "unpublish stream error: %s", err)
|
slog.Error("unpublish stream error", "error", err, "stream_id", session.Ssrc)
|
||||||
}
|
}
|
||||||
s.RemoveSession(key)
|
s.RemoveSession(key)
|
||||||
}()
|
}()
|
||||||
@ -523,7 +523,11 @@ func (s *UAS) QueryRecord(deviceID, channelID string, startTime, endTime int64)
|
|||||||
return nil, errors.Errorf("context done")
|
return nil, errors.Errorf("context done")
|
||||||
case records := <-resultChan:
|
case records := <-resultChan:
|
||||||
allRecords = append(allRecords, records.RecordList...)
|
allRecords = append(allRecords, records.RecordList...)
|
||||||
logger.Tf(s.ctx, "[channel %s] 应收总数 %d, 实收总数 %d, 本次收到 %d", channelID, records.SumNum, len(allRecords), len(records.RecordList))
|
slog.Info("Record query result",
|
||||||
|
"channel", channelID,
|
||||||
|
"expected_count", records.SumNum,
|
||||||
|
"actual_count", len(allRecords),
|
||||||
|
"batch_count", len(records.RecordList))
|
||||||
|
|
||||||
if len(allRecords) == records.SumNum {
|
if len(allRecords) == records.SumNum {
|
||||||
return allRecords, nil
|
return allRecords, nil
|
||||||
|
|||||||
@ -3,11 +3,11 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
"github.com/emiago/sipgo"
|
"github.com/emiago/sipgo"
|
||||||
"github.com/emiago/sipgo/sip"
|
"github.com/emiago/sipgo/sip"
|
||||||
"github.com/ossrs/go-oryx-lib/errors"
|
"github.com/ossrs/go-oryx-lib/errors"
|
||||||
"github.com/ossrs/go-oryx-lib/logger"
|
|
||||||
"github.com/ossrs/srs-sip/pkg/config"
|
"github.com/ossrs/srs-sip/pkg/config"
|
||||||
"github.com/ossrs/srs-sip/pkg/service/stack"
|
"github.com/ossrs/srs-sip/pkg/service/stack"
|
||||||
)
|
)
|
||||||
@ -26,7 +26,7 @@ type UAC struct {
|
|||||||
func NewUac() *UAC {
|
func NewUac() *UAC {
|
||||||
ip, err := config.GetLocalIP()
|
ip, err := config.GetLocalIP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.E("get local ip failed")
|
slog.Error("get local ip failed", "error", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ func (c *UAC) doRegister() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rs, _ := c.getResponse(tx)
|
rs, _ := c.getResponse(tx)
|
||||||
logger.Tf(c.ctx, "register response: %s", rs.String())
|
slog.Info("register response", "response", rs.String())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,15 +101,15 @@ func (c *UAC) OnRequest(req *sip.Request, tx sip.ServerTransaction) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *UAC) onInvite(req *sip.Request, tx sip.ServerTransaction) {
|
func (c *UAC) onInvite(req *sip.Request, tx sip.ServerTransaction) {
|
||||||
logger.T(c.ctx, "onInvite")
|
slog.Debug("onInvite")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *UAC) onBye(req *sip.Request, tx sip.ServerTransaction) {
|
func (c *UAC) onBye(req *sip.Request, tx sip.ServerTransaction) {
|
||||||
logger.T(c.ctx, "onBye")
|
slog.Debug("onBye")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *UAC) onMessage(req *sip.Request, tx sip.ServerTransaction) {
|
func (c *UAC) onMessage(req *sip.Request, tx sip.ServerTransaction) {
|
||||||
logger.Tf(c.ctx, "onMessage %s", req.String())
|
slog.Debug("onMessage", "request", req.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *UAC) getResponse(tx sip.ClientTransaction) (*sip.Response, error) {
|
func (c *UAC) getResponse(tx sip.ClientTransaction) (*sip.Response, error) {
|
||||||
|
|||||||
@ -4,12 +4,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/emiago/sipgo"
|
"github.com/emiago/sipgo"
|
||||||
"github.com/ossrs/go-oryx-lib/logger"
|
|
||||||
"github.com/ossrs/srs-sip/pkg/config"
|
"github.com/ossrs/srs-sip/pkg/config"
|
||||||
"github.com/ossrs/srs-sip/pkg/db"
|
"github.com/ossrs/srs-sip/pkg/db"
|
||||||
"github.com/ossrs/srs-sip/pkg/media"
|
"github.com/ossrs/srs-sip/pkg/media"
|
||||||
@ -100,7 +100,7 @@ func (s *UAS) startUDP() error {
|
|||||||
return fmt.Errorf("cannot listen on the UDP signaling port %d: %w", s.conf.GB28181.Port, err)
|
return fmt.Errorf("cannot listen on the UDP signaling port %d: %w", s.conf.GB28181.Port, err)
|
||||||
}
|
}
|
||||||
s.sipConnUDP = lis
|
s.sipConnUDP = lis
|
||||||
logger.Tf(s.ctx, "sip signaling listening on UDP %s:%d", lis.LocalAddr().String(), s.conf.GB28181.Port)
|
slog.Info("sip signaling listening on UDP", "address", lis.LocalAddr().String(), "port", s.conf.GB28181.Port)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := s.sipSvr.ServeUDP(lis); err != nil {
|
if err := s.sipSvr.ServeUDP(lis); err != nil {
|
||||||
@ -119,7 +119,7 @@ func (s *UAS) startTCP() error {
|
|||||||
return fmt.Errorf("cannot listen on the TCP signaling port %d: %w", s.conf.GB28181.Port, err)
|
return fmt.Errorf("cannot listen on the TCP signaling port %d: %w", s.conf.GB28181.Port, err)
|
||||||
}
|
}
|
||||||
s.sipConnTCP = lis
|
s.sipConnTCP = lis
|
||||||
logger.Tf(s.ctx, "sip signaling listening on TCP %s:%d", lis.Addr().String(), s.conf.GB28181.Port)
|
slog.Info("sip signaling listening on TCP", "address", lis.Addr().String(), "port", s.conf.GB28181.Port)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := s.sipSvr.ServeTCP(lis); err != nil && !errors.Is(err, net.ErrClosed) {
|
if err := s.sipSvr.ServeTCP(lis); err != nil && !errors.Is(err, net.ErrClosed) {
|
||||||
|
|||||||
201
pkg/utils/logger.go
Normal file
201
pkg/utils/logger.go
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var logLevelMap = map[string]slog.Level{
|
||||||
|
"debug": slog.LevelDebug,
|
||||||
|
"info": slog.LevelInfo,
|
||||||
|
"warn": slog.LevelWarn,
|
||||||
|
"error": slog.LevelError,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义格式处理器,以 [时间] [级别] [消息] 格式输出日志
|
||||||
|
type CustomFormatHandler struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
w io.Writer
|
||||||
|
level slog.Level
|
||||||
|
attrs []slog.Attr
|
||||||
|
groups []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCustomFormatHandler 创建一个新的自定义格式处理器
|
||||||
|
func NewCustomFormatHandler(w io.Writer, opts *slog.HandlerOptions) *CustomFormatHandler {
|
||||||
|
if opts == nil {
|
||||||
|
opts = &slog.HandlerOptions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取日志级别,如果opts.Level是nil则默认为Info
|
||||||
|
var level slog.Level
|
||||||
|
if opts.Level != nil {
|
||||||
|
level = opts.Level.Level()
|
||||||
|
} else {
|
||||||
|
level = slog.LevelInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CustomFormatHandler{
|
||||||
|
w: w,
|
||||||
|
level: level,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled 实现 slog.Handler 接口
|
||||||
|
func (h *CustomFormatHandler) Enabled(ctx context.Context, level slog.Level) bool {
|
||||||
|
return level >= h.level
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle 实现 slog.Handler 接口,以自定义格式输出日志
|
||||||
|
func (h *CustomFormatHandler) Handle(ctx context.Context, record slog.Record) error {
|
||||||
|
h.mu.Lock()
|
||||||
|
defer h.mu.Unlock()
|
||||||
|
|
||||||
|
// 时间格式
|
||||||
|
timeStr := record.Time.Format("2006-01-02 15:04:05.000")
|
||||||
|
|
||||||
|
// 日志级别
|
||||||
|
var levelStr string
|
||||||
|
switch {
|
||||||
|
case record.Level >= slog.LevelError:
|
||||||
|
levelStr = "ERROR"
|
||||||
|
case record.Level >= slog.LevelWarn:
|
||||||
|
levelStr = "WARN "
|
||||||
|
case record.Level >= slog.LevelInfo:
|
||||||
|
levelStr = "INFO "
|
||||||
|
default:
|
||||||
|
levelStr = "DEBUG"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建日志行
|
||||||
|
logLine := fmt.Sprintf("[%s] [%s] %s", timeStr, levelStr, record.Message)
|
||||||
|
|
||||||
|
// 处理其他属性
|
||||||
|
var attrs []string
|
||||||
|
record.Attrs(func(attr slog.Attr) bool {
|
||||||
|
attrs = append(attrs, fmt.Sprintf("%s=%v", attr.Key, attr.Value))
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(attrs) > 0 {
|
||||||
|
logLine += " " + strings.Join(attrs, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入日志
|
||||||
|
_, err := fmt.Fprintln(h.w, logLine)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithAttrs 实现 slog.Handler 接口
|
||||||
|
func (h *CustomFormatHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||||
|
h2 := *h
|
||||||
|
h2.attrs = append(h.attrs[:], attrs...)
|
||||||
|
return &h2
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithGroup 实现 slog.Handler 接口
|
||||||
|
func (h *CustomFormatHandler) WithGroup(name string) slog.Handler {
|
||||||
|
h2 := *h
|
||||||
|
h2.groups = append(h.groups[:], name)
|
||||||
|
return &h2
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultiHandler 实现了 slog.Handler 接口,将日志同时发送到多个处理器
|
||||||
|
type MultiHandler struct {
|
||||||
|
handlers []slog.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled 实现 slog.Handler 接口
|
||||||
|
func (h *MultiHandler) Enabled(ctx context.Context, level slog.Level) bool {
|
||||||
|
// 如果任何一个处理器启用了该级别,则返回 true
|
||||||
|
for _, handler := range h.handlers {
|
||||||
|
if handler.Enabled(ctx, level) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle 实现 slog.Handler 接口
|
||||||
|
func (h *MultiHandler) Handle(ctx context.Context, record slog.Record) error {
|
||||||
|
// 将记录发送到所有处理器
|
||||||
|
for _, handler := range h.handlers {
|
||||||
|
if handler.Enabled(ctx, record.Level) {
|
||||||
|
if err := handler.Handle(ctx, record); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithAttrs 实现 slog.Handler 接口
|
||||||
|
func (h *MultiHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||||
|
newHandlers := make([]slog.Handler, len(h.handlers))
|
||||||
|
for i, handler := range h.handlers {
|
||||||
|
newHandlers[i] = handler.WithAttrs(attrs)
|
||||||
|
}
|
||||||
|
return &MultiHandler{handlers: newHandlers}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithGroup 实现 slog.Handler 接口
|
||||||
|
func (h *MultiHandler) WithGroup(name string) slog.Handler {
|
||||||
|
newHandlers := make([]slog.Handler, len(h.handlers))
|
||||||
|
for i, handler := range h.handlers {
|
||||||
|
newHandlers[i] = handler.WithGroup(name)
|
||||||
|
}
|
||||||
|
return &MultiHandler{handlers: newHandlers}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupLogger 设置日志输出
|
||||||
|
func SetupLogger(logLevel string, logFile string) error {
|
||||||
|
// 创建标准错误输出的处理器,使用自定义格式
|
||||||
|
stdHandler := NewCustomFormatHandler(os.Stderr, &slog.HandlerOptions{
|
||||||
|
Level: logLevelMap[logLevel],
|
||||||
|
})
|
||||||
|
|
||||||
|
// 如果没有指定日志文件,则仅使用标准错误处理器
|
||||||
|
if logFile == "" {
|
||||||
|
slog.SetDefault(slog.New(stdHandler))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保日志文件所在目录存在
|
||||||
|
logDir := filepath.Dir(logFile)
|
||||||
|
if err := os.MkdirAll(logDir, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开日志文件,如果不存在则创建,追加写入模式
|
||||||
|
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建文件输出的处理器,使用自定义格式
|
||||||
|
fileHandler := NewCustomFormatHandler(file, &slog.HandlerOptions{
|
||||||
|
Level: logLevelMap[logLevel],
|
||||||
|
})
|
||||||
|
|
||||||
|
// 创建多输出处理器
|
||||||
|
multiHandler := &MultiHandler{
|
||||||
|
handlers: []slog.Handler{stdHandler, fileHandler},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置全局日志处理器
|
||||||
|
slog.SetDefault(slog.New(multiHandler))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitDefaultLogger 初始化默认日志处理器
|
||||||
|
func InitDefaultLogger(level slog.Level) {
|
||||||
|
slog.SetDefault(slog.New(NewCustomFormatHandler(os.Stderr, &slog.HandlerOptions{
|
||||||
|
Level: level,
|
||||||
|
})))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user