Implement sighandler, logfile can be not only stderr
* Implement sighandler for int, quit, term signals * Implement log reopen on sighup * Catch http.Server's error log * Implement logfile setting in config
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
version: "2"
|
version: "2"
|
||||||
run:
|
run:
|
||||||
go: "1.25"
|
go: "1.24"
|
||||||
modules-download-mode: readonly
|
modules-download-mode: readonly
|
||||||
tests: false
|
tests: false
|
||||||
allow-parallel-runners: true
|
allow-parallel-runners: true
|
||||||
@@ -196,7 +196,51 @@ linters:
|
|||||||
- fmt
|
- fmt
|
||||||
# https://staticcheck.dev/docs/configuration/options/#initialisms
|
# https://staticcheck.dev/docs/configuration/options/#initialisms
|
||||||
# Default: ["ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS", "SIP", "RTP", "AMQP", "DB", "TS"]
|
# Default: ["ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS", "SIP", "RTP", "AMQP", "DB", "TS"]
|
||||||
initialisms: ["ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS", "SIP", "RTP", "AMQP", "DB", "TS"]
|
initialisms:
|
||||||
|
[
|
||||||
|
"ACL",
|
||||||
|
"API",
|
||||||
|
"ASCII",
|
||||||
|
"CPU",
|
||||||
|
"CSS",
|
||||||
|
"DNS",
|
||||||
|
"EOF",
|
||||||
|
"GUID",
|
||||||
|
"HTML",
|
||||||
|
"HTTP",
|
||||||
|
"HTTPS",
|
||||||
|
"ID",
|
||||||
|
"IP",
|
||||||
|
"JSON",
|
||||||
|
"QPS",
|
||||||
|
"RAM",
|
||||||
|
"RPC",
|
||||||
|
"SLA",
|
||||||
|
"SMTP",
|
||||||
|
"SQL",
|
||||||
|
"SSH",
|
||||||
|
"TCP",
|
||||||
|
"TLS",
|
||||||
|
"TTL",
|
||||||
|
"UDP",
|
||||||
|
"UI",
|
||||||
|
"GID",
|
||||||
|
"UID",
|
||||||
|
"UUID",
|
||||||
|
"URI",
|
||||||
|
"URL",
|
||||||
|
"UTF8",
|
||||||
|
"VM",
|
||||||
|
"XML",
|
||||||
|
"XMPP",
|
||||||
|
"XSRF",
|
||||||
|
"XSS",
|
||||||
|
"SIP",
|
||||||
|
"RTP",
|
||||||
|
"AMQP",
|
||||||
|
"DB",
|
||||||
|
"TS",
|
||||||
|
]
|
||||||
# https://staticcheck.dev/docs/configuration/options/#http_status_code_whitelist
|
# https://staticcheck.dev/docs/configuration/options/#http_status_code_whitelist
|
||||||
# Default: ["200", "400", "404", "500"]
|
# Default: ["200", "400", "404", "500"]
|
||||||
http-status-code-whitelist: ["200", "400", "404", "500"]
|
http-status-code-whitelist: ["200", "400", "404", "500"]
|
||||||
|
|||||||
@@ -2,8 +2,11 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"joyproxy/internal/joyproxy"
|
"joyproxy/internal/joyproxy"
|
||||||
@@ -12,26 +15,74 @@ import (
|
|||||||
|
|
||||||
// main application entry point.
|
// main application entry point.
|
||||||
func main() {
|
func main() {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
logfile *os.File
|
||||||
|
)
|
||||||
|
|
||||||
joyproxy.LoadConf()
|
joyproxy.LoadConf()
|
||||||
|
|
||||||
log.Init(joyproxy.Cfg.Loglevel, os.Stderr)
|
if joyproxy.Cfg.Logfile != "" {
|
||||||
|
logfile, err = os.OpenFile(joyproxy.Cfg.Logfile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Unable to open %s: %s", joyproxy.Cfg.Logfile, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logfile = os.Stderr
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Init(joyproxy.Cfg.Loglevel, logfile)
|
||||||
|
|
||||||
|
log.Info("Install handler to /joyproxy/ location")
|
||||||
http.HandleFunc("/joyproxy/", joyproxy.JoyproxyHandler)
|
http.HandleFunc("/joyproxy/", joyproxy.JoyproxyHandler)
|
||||||
|
|
||||||
|
log.Info("Install handler to /joyurl location")
|
||||||
http.HandleFunc("/joyurl", joyproxy.JoyurlHandler)
|
http.HandleFunc("/joyurl", joyproxy.JoyurlHandler)
|
||||||
|
|
||||||
log.Infof("Starting application on port %v\n", joyproxy.Cfg.Port)
|
// It's time to set up signal trapper.
|
||||||
|
signal.Notify(joyproxy.SigChan,
|
||||||
|
syscall.SIGINT,
|
||||||
|
syscall.SIGTERM,
|
||||||
|
syscall.SIGQUIT,
|
||||||
|
syscall.SIGHUP,
|
||||||
|
)
|
||||||
|
|
||||||
|
go joyproxy.SigHandler()
|
||||||
|
|
||||||
|
log.Infof("Listening on port %v\n", joyproxy.Cfg.Port)
|
||||||
|
|
||||||
// To prevent slow loris ddos, we have to define custom handler because builtin http.ListenAndServe() have no
|
// To prevent slow loris ddos, we have to define custom handler because builtin http.ListenAndServe() have no
|
||||||
// timeout and sits here forever.
|
// timeout and sits here forever.
|
||||||
server := &http.Server{
|
joyproxy.Server = &http.Server{
|
||||||
Addr: joyproxy.Cfg.Port,
|
Addr: joyproxy.Cfg.Port,
|
||||||
ReadHeaderTimeout: time.Duration(joyproxy.Cfg.Timeout) * time.Second,
|
ReadHeaderTimeout: time.Duration(joyproxy.Cfg.Timeout) * time.Second,
|
||||||
ReadTimeout: time.Duration(joyproxy.Cfg.Timeout) * time.Second,
|
ReadTimeout: time.Duration(joyproxy.Cfg.Timeout) * time.Second,
|
||||||
WriteTimeout: time.Duration(joyproxy.Cfg.Timeout) * time.Second,
|
WriteTimeout: time.Duration(joyproxy.Cfg.Timeout) * time.Second,
|
||||||
IdleTimeout: time.Duration(joyproxy.Cfg.Timeout) * time.Second,
|
IdleTimeout: time.Duration(joyproxy.Cfg.Timeout) * time.Second,
|
||||||
|
ErrorLog: &log.Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Warn(server.ListenAndServe().Error())
|
for {
|
||||||
|
if joyproxy.Shutdown {
|
||||||
|
time.Sleep(100 * time.Microsecond)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = joyproxy.Server.ListenAndServe()
|
||||||
|
|
||||||
|
if errors.Is(err, http.ErrServerClosed) {
|
||||||
|
log.Info("Shutdown joyproxy")
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Errorf("Listener error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
<-joyproxy.QuitChan
|
||||||
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
// Package joyproxy is main appliction lib.
|
// Package joyproxy is main appliction lib.
|
||||||
package joyproxy
|
package joyproxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Cfg represents struct with application config settings.
|
// Cfg represents struct with application config settings.
|
||||||
Cfg Config
|
Cfg Config
|
||||||
@@ -42,6 +47,18 @@ var (
|
|||||||
}
|
}
|
||||||
|
|
||||||
postForm = "<html>\n<body>\n<form method='get' action='joyurl'>\n<input type='text' name='joyurl' size=100 autofocus><br>\n<input type='submit' value='Post it!' style='font-size:115%%;'>\n<br>%s<br>\n</body>\n</html>\n"
|
postForm = "<html>\n<body>\n<form method='get' action='joyurl'>\n<input type='text' name='joyurl' size=100 autofocus><br>\n<input type='submit' value='Post it!' style='font-size:115%%;'>\n<br>%s<br>\n</body>\n</html>\n"
|
||||||
|
|
||||||
|
// SigChan is channel for signal handler routine SigHandler().
|
||||||
|
SigChan = make(chan os.Signal, 1)
|
||||||
|
|
||||||
|
// Server represents http server object.
|
||||||
|
Server *http.Server
|
||||||
|
|
||||||
|
// Shutdown set to true if service is shutting down via SigHandler().
|
||||||
|
Shutdown bool
|
||||||
|
|
||||||
|
// QuitChan is locking mechanism to wait until shutdown procedure completes.
|
||||||
|
QuitChan = make(chan bool, 1)
|
||||||
)
|
)
|
||||||
|
|
||||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
|
|||||||
@@ -14,12 +14,13 @@ import (
|
|||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
)
|
)
|
||||||
|
|
||||||
// JoyproxyHandler request handler for /joyproxy location.
|
// JoyproxyHandler request handler for /joyproxy location. It reads data from reactor.cc and immediately streams it to
|
||||||
|
// client.
|
||||||
func JoyproxyHandler(w http.ResponseWriter, r *http.Request) { //nolint: revive
|
func JoyproxyHandler(w http.ResponseWriter, r *http.Request) { //nolint: revive
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if r.Method != http.MethodGet {
|
if r.Method != http.MethodGet {
|
||||||
log.Infof("request method is not GET, but %s", r.Method)
|
log.Infof("Request method is not GET, but %s, sending 404 to client", r.Method)
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
|
||||||
if _, err = w.Write([]byte("404 Not Found")); err != nil {
|
if _, err = w.Write([]byte("404 Not Found")); err != nil {
|
||||||
@@ -35,7 +36,7 @@ func JoyproxyHandler(w http.ResponseWriter, r *http.Request) { //nolint: revive
|
|||||||
joyHost := joyPath[2]
|
joyHost := joyPath[2]
|
||||||
|
|
||||||
if !regexp.MustCompile("^img[0-9]+[.]reactor[.]cc$").MatchString(joyHost) {
|
if !regexp.MustCompile("^img[0-9]+[.]reactor[.]cc$").MatchString(joyHost) {
|
||||||
log.Infof("supplied proxy target host is not match with ^img[0-9]+[.]reactor[.]cc$ pattern: %s", joyHost)
|
log.Infof("Supplied proxy target host is not match with ^img[0-9]+[.]reactor[.]cc$ pattern: %s, sening 404 to client", joyHost)
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
|
||||||
if _, err = w.Write([]byte("404 Not Found")); err != nil {
|
if _, err = w.Write([]byte("404 Not Found")); err != nil {
|
||||||
@@ -49,7 +50,7 @@ func JoyproxyHandler(w http.ResponseWriter, r *http.Request) { //nolint: revive
|
|||||||
dlReq.RequestURI = "/" + strings.Join(joyPath, "/")
|
dlReq.RequestURI = "/" + strings.Join(joyPath, "/")
|
||||||
|
|
||||||
if !regexp.MustCompile("^/pics/post/mp4/.+[.]mp4$").MatchString(dlReq.RequestURI) {
|
if !regexp.MustCompile("^/pics/post/mp4/.+[.]mp4$").MatchString(dlReq.RequestURI) {
|
||||||
log.Infof("supplied uri is not match with /pics/post/mp4/.+[.]mp4$ pattern: %s", dlReq.RequestURI)
|
log.Infof("Supplied uri is not match with /pics/post/mp4/.+[.]mp4$ pattern: %s, sending 404 to client", dlReq.RequestURI)
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
|
||||||
if _, err = w.Write([]byte("404 Not Found")); err != nil {
|
if _, err = w.Write([]byte("404 Not Found")); err != nil {
|
||||||
@@ -63,7 +64,7 @@ func JoyproxyHandler(w http.ResponseWriter, r *http.Request) { //nolint: revive
|
|||||||
dlReq.URL, err = url.Parse(dlReq.RequestURI)
|
dlReq.URL, err = url.Parse(dlReq.RequestURI)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Infof("unable to parse %s via url.Parse(): %s", dlReq.RequestURI, err)
|
log.Infof("Unable to parse %s via url.Parse(): %s, sending 404 to client", dlReq.RequestURI, err)
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
|
||||||
if _, err = w.Write([]byte("404 Not Found")); err != nil {
|
if _, err = w.Write([]byte("404 Not Found")); err != nil {
|
||||||
@@ -78,13 +79,16 @@ func JoyproxyHandler(w http.ResponseWriter, r *http.Request) { //nolint: revive
|
|||||||
dlReq.Header.Del("Referer")
|
dlReq.Header.Del("Referer")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debugf("Replacing/appending Referer header value with https://old.reactor.cc/all")
|
||||||
dlReq.Header.Add("Referer", "https://old.reactor.cc/all")
|
dlReq.Header.Add("Referer", "https://old.reactor.cc/all")
|
||||||
|
|
||||||
if dlReq.Header.Get("User-Agent") != "" {
|
if dlReq.Header.Get("User-Agent") != "" {
|
||||||
dlReq.Header.Del("User-Agent")
|
dlReq.Header.Del("User-Agent")
|
||||||
}
|
}
|
||||||
|
|
||||||
dlReq.Header.Add("User-Agent", userAgentString[rand.Intn(len(userAgentString)-1)])
|
ua := userAgentString[rand.Intn(len(userAgentString)-1)]
|
||||||
|
log.Debugf("Replacing User-Agent header value with %s", ua)
|
||||||
|
dlReq.Header.Add("User-Agent", ua)
|
||||||
|
|
||||||
if dlReq.Header.Get("Accept") != "" {
|
if dlReq.Header.Get("Accept") != "" {
|
||||||
dlReq.Header.Del("Accept")
|
dlReq.Header.Del("Accept")
|
||||||
@@ -108,6 +112,7 @@ func JoyproxyHandler(w http.ResponseWriter, r *http.Request) { //nolint: revive
|
|||||||
dlReq.Header.Del("Range")
|
dlReq.Header.Del("Range")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debugf("Replacing/appending Range header value with bytes=0-")
|
||||||
dlReq.Header.Add("Range", "bytes=0-")
|
dlReq.Header.Add("Range", "bytes=0-")
|
||||||
log.Debug(spew.Sdump(dlReq))
|
log.Debug(spew.Sdump(dlReq))
|
||||||
|
|
||||||
@@ -115,7 +120,7 @@ func JoyproxyHandler(w http.ResponseWriter, r *http.Request) { //nolint: revive
|
|||||||
target, err := url.Parse(u)
|
target, err := url.Parse(u)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Infof("unable to parse %s via url.Parse(): %s", u, err)
|
log.Infof("Unable to parse %s via url.Parse(): %s, sending 404 to client", u, err)
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
|
||||||
if _, err = w.Write([]byte("404 Not Found")); err != nil {
|
if _, err = w.Write([]byte("404 Not Found")); err != nil {
|
||||||
@@ -133,7 +138,10 @@ func JoyproxyHandler(w http.ResponseWriter, r *http.Request) { //nolint: revive
|
|||||||
KeepAlive: time.Duration(Cfg.Timeout) * time.Second,
|
KeepAlive: time.Duration(Cfg.Timeout) * time.Second,
|
||||||
}).Dial,
|
}).Dial,
|
||||||
TLSHandshakeTimeout: time.Duration(Cfg.Timeout) * time.Second,
|
TLSHandshakeTimeout: time.Duration(Cfg.Timeout) * time.Second,
|
||||||
|
DisableKeepAlives: true,
|
||||||
|
DisableCompression: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
joyproxy.ServeHTTP(w, dlReq)
|
joyproxy.ServeHTTP(w, dlReq)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,19 +18,19 @@ func JoyurlHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
proxyURLString = " "
|
proxyURLString = " "
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Debug(spew.Sdump(param))
|
log.Debugf("Parse url into chunks: %s", spew.Sdump(param))
|
||||||
|
|
||||||
re := regexp.MustCompile("^https?://img[0-9]+[.]reactor[.]cc/.+[.][Ww][Ee][Bb][Mm]$")
|
re := regexp.MustCompile("^https?://img[0-9]+[.]reactor[.]cc/.+[.][Ww][Ee][Bb][Mm]$")
|
||||||
|
|
||||||
if len(param) > 0 && re.MatchString(param["joyurl"][0]) {
|
if len(param) > 0 && re.MatchString(param["joyurl"][0]) {
|
||||||
log.Debug("match with ^https?://img[0-9]+[.]reactor[.]cc/.+[.][Ww][Ee][Bb][Mm]$ pattern")
|
log.Debug("Url match with ^https?://img[0-9]+[.]reactor[.]cc/.+[.][Ww][Ee][Bb][Mm]$ pattern")
|
||||||
// https://img1.reactor.cc/pics/post/webm/видосик.webm
|
// https://img1.reactor.cc/pics/post/webm/видосик.webm
|
||||||
|
|
||||||
p := regexp.MustCompile("/").Split(param["joyurl"][0], -1)
|
p := regexp.MustCompile("/").Split(param["joyurl"][0], -1)
|
||||||
log.Debug(spew.Sdump(p))
|
log.Debugf("Split url string by / delimiter: %s", spew.Sdump(p))
|
||||||
|
|
||||||
if len(p) > 6 && p[5] == "webm" {
|
if len(p) > 6 && p[5] == "webm" {
|
||||||
log.Debug("webm url part detected, let's make joyproxy url")
|
log.Debug("Substring webm is detected in url, let's make joyproxy url")
|
||||||
|
|
||||||
file := p[6][:len(p[6])-5]
|
file := p[6][:len(p[6])-5]
|
||||||
|
|
||||||
@@ -46,10 +46,10 @@ func JoyurlHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
proxyURLString = html.EscapeString(proxyURLString)
|
proxyURLString = html.EscapeString(proxyURLString)
|
||||||
} else {
|
} else {
|
||||||
log.Debug("webm url part not detected, skip making joyproxy url")
|
log.Debug("Substring webm not detected in url, skip making joyproxy url")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Info("not match with ^https?://img[0-9]+[.]reactor[.]cc/.+[.][Ww][Ee][Bb][Mm]$ regex")
|
log.Info("Url is not match with ^https?://img[0-9]+[.]reactor[.]cc/.+[.][Ww][Ee][Bb][Mm]$ regex, skipping")
|
||||||
}
|
}
|
||||||
|
|
||||||
htmlText := fmt.Sprintf(postForm, proxyURLString)
|
htmlText := fmt.Sprintf(postForm, proxyURLString)
|
||||||
|
|||||||
@@ -52,6 +52,12 @@ func LoadConf() {
|
|||||||
if Cfg.Loglevel == "" {
|
if Cfg.Loglevel == "" {
|
||||||
log.Fatalf("unable to get loglevel setting from settings section of %s", configINIPath)
|
log.Fatalf("unable to get loglevel setting from settings section of %s", configINIPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Cfg.Logfile = configFH.Section("settings").Get("logfile")
|
||||||
|
|
||||||
|
if Cfg.Logfile == "" {
|
||||||
|
log.Infof("Logfile parameter is not set in settings section of %s", configINIPath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
|
|||||||
44
internal/joyproxy/sigHandler.go
Normal file
44
internal/joyproxy/sigHandler.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package joyproxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"joyproxy/internal/log"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SigHandler handles signals from OS.
|
||||||
|
func SigHandler() {
|
||||||
|
for {
|
||||||
|
var s = <-SigChan
|
||||||
|
switch s {
|
||||||
|
case syscall.SIGINT:
|
||||||
|
log.Info("Got SIGINT, quitting")
|
||||||
|
case syscall.SIGTERM:
|
||||||
|
log.Info("Got SIGTERM, quitting")
|
||||||
|
case syscall.SIGQUIT:
|
||||||
|
log.Info("Got SIGQUIT, quitting")
|
||||||
|
case syscall.SIGHUP:
|
||||||
|
log.Info("Got SIGHUP, reopening logs")
|
||||||
|
log.ReOpenLog()
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
// Make new iteration since we 've got signal we not interested in.
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
Shutdown = true
|
||||||
|
|
||||||
|
if err := Server.Close(); err != nil {
|
||||||
|
log.Errorf("Unable to close server: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Close()
|
||||||
|
|
||||||
|
QuitChan <- true
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
@@ -6,6 +6,7 @@ type (
|
|||||||
Port string
|
Port string
|
||||||
Proto string
|
Proto string
|
||||||
Loglevel string
|
Loglevel string
|
||||||
|
Logfile string
|
||||||
Timeout int
|
Timeout int
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
@@ -24,30 +25,39 @@ var (
|
|||||||
|
|
||||||
// reader is required for Writer to work properly. It is connected via io.Pipe() to Writer.
|
// reader is required for Writer to work properly. It is connected via io.Pipe() to Writer.
|
||||||
reader io.Reader
|
reader io.Reader
|
||||||
|
|
||||||
|
// Logger *log.Logger thing required for compatibility with old-good log package.
|
||||||
|
Logger log.Logger
|
||||||
|
|
||||||
|
// LogFileHandler is currently opened log file descriptor. Stderr by default.
|
||||||
|
LogFileHandler = os.Stderr
|
||||||
|
|
||||||
|
// LogLevel current loglevel. Info by default.
|
||||||
|
LogLevel = slog.LevelInfo
|
||||||
)
|
)
|
||||||
|
|
||||||
// Init setup logger stuff.
|
// Init setup logger stuff.
|
||||||
// level can be error, warn, info, debug if something other supplied info level selected.
|
// level can be error, warn, info, debug if something other supplied info level selected.
|
||||||
// fileDescriptor should be opened before supplying it to Init().
|
// fileDescriptor should be opened before supplying it to Init().
|
||||||
func Init(level string, fileDescriptor *os.File) {
|
func Init(level string, fileDescriptor *os.File) {
|
||||||
var loglevel slog.Level
|
LogFileHandler = fileDescriptor
|
||||||
|
|
||||||
// no panic, no trace.
|
// no panic, no trace.
|
||||||
switch level {
|
switch level {
|
||||||
case "debug":
|
case "debug":
|
||||||
loglevel = slog.LevelDebug
|
LogLevel = slog.LevelDebug
|
||||||
|
|
||||||
case "info":
|
case "info":
|
||||||
loglevel = slog.LevelInfo
|
LogLevel = slog.LevelInfo
|
||||||
|
|
||||||
case "warn":
|
case "warn":
|
||||||
loglevel = slog.LevelWarn
|
LogLevel = slog.LevelWarn
|
||||||
|
|
||||||
case "error":
|
case "error":
|
||||||
loglevel = slog.LevelError
|
LogLevel = slog.LevelError
|
||||||
|
|
||||||
default:
|
default:
|
||||||
loglevel = slog.LevelInfo
|
LogLevel = slog.LevelInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := &slog.HandlerOptions{ //nolint: exhaustruct
|
opts := &slog.HandlerOptions{ //nolint: exhaustruct
|
||||||
@@ -68,15 +78,22 @@ func Init(level string, fileDescriptor *os.File) {
|
|||||||
return a
|
return a
|
||||||
},
|
},
|
||||||
|
|
||||||
Level: loglevel,
|
Level: LogLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
Handler = slog.NewTextHandler(fileDescriptor, opts)
|
// Documentation says that we can use *os.File in place of io.Writer, but in this case it did not work.
|
||||||
|
// So we have to cast fileDescriptor to io.Writer.
|
||||||
|
w := io.Writer(fileDescriptor)
|
||||||
|
|
||||||
|
Handler = slog.NewTextHandler(w, opts)
|
||||||
|
|
||||||
slog.SetDefault(
|
slog.SetDefault(
|
||||||
slog.New(Handler),
|
slog.New(Handler),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Catch log.Logger message at info loglevel of slog logging facility.
|
||||||
|
Logger = *slog.NewLogLogger(Handler, slog.LevelError)
|
||||||
|
|
||||||
reader, Writer = io.Pipe()
|
reader, Writer = io.Pipe()
|
||||||
|
|
||||||
go readIoPipe()
|
go readIoPipe()
|
||||||
@@ -104,7 +121,7 @@ func readIoPipe() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLevel returns current loglevel.
|
// GetLevel returns current loglevel as string.
|
||||||
func GetLevel() string {
|
func GetLevel() string {
|
||||||
switch {
|
switch {
|
||||||
case Handler.Enabled(Ctx, slog.LevelDebug):
|
case Handler.Enabled(Ctx, slog.LevelDebug):
|
||||||
@@ -176,4 +193,40 @@ func Debugf(format string, a ...any) {
|
|||||||
slog.Debug(fmt.Sprintf(format, a...))
|
slog.Debug(fmt.Sprintf(format, a...))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close closes opened log file descriptors.
|
||||||
|
func Close() {
|
||||||
|
LogFileHandler.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReOpenLog closes and opens log file again.
|
||||||
|
func ReOpenLog() {
|
||||||
|
filename := LogFileHandler.Name()
|
||||||
|
|
||||||
|
// If we writing to stderr, we should not re-open it.
|
||||||
|
if filename == os.Stderr.Name() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loglevel := GetLevel()
|
||||||
|
|
||||||
|
LogFileHandler.Close()
|
||||||
|
|
||||||
|
file, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// Try to emulate our own log message format.
|
||||||
|
fmt.Fprintf(
|
||||||
|
os.Stderr,
|
||||||
|
"time=\"%s\" level=ERROR msg=\"Unable to open logfile %s: %s\"",
|
||||||
|
time.Now().Format(time.DateTime),
|
||||||
|
filename,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Init(loglevel, file)
|
||||||
|
}
|
||||||
|
|
||||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
|
|||||||
@@ -9,4 +9,7 @@ port = 9980
|
|||||||
timeout = 5
|
timeout = 5
|
||||||
|
|
||||||
; loglevel - info or debug, info is what you want
|
; loglevel - info or debug, info is what you want
|
||||||
loglevel = debug
|
loglevel = info
|
||||||
|
|
||||||
|
; logfile - log to this file, default stderr
|
||||||
|
;logfile = /var/log/joyproxy.log
|
||||||
|
|||||||
Reference in New Issue
Block a user