some initial code
This commit is contained in:
272
cmd/logger.go
Normal file
272
cmd/logger.go
Normal file
@ -0,0 +1,272 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
import "haza"
|
||||
import "io"
|
||||
import "os"
|
||||
import "path/filepath"
|
||||
import "runtime"
|
||||
import "strings"
|
||||
import "sync"
|
||||
import "sync/atomic"
|
||||
import "syscall"
|
||||
import "time"
|
||||
|
||||
type app_logger_msg_t struct {
|
||||
code int
|
||||
data string
|
||||
}
|
||||
|
||||
type AppLogger struct {
|
||||
id string
|
||||
out io.Writer
|
||||
mask haza.LogMask
|
||||
|
||||
file *os.File
|
||||
file_name string // you can get the file name from file but this is to preserve the original.
|
||||
file_rotate int
|
||||
file_max_size int64
|
||||
msg_chan chan app_logger_msg_t
|
||||
wg sync.WaitGroup
|
||||
|
||||
use_color bool
|
||||
closed atomic.Bool
|
||||
}
|
||||
|
||||
func _is_ansi_tty(fd uintptr) bool {
|
||||
var st syscall.Stat_t
|
||||
var err error
|
||||
|
||||
err = syscall.Fstat(int(fd), &st)
|
||||
if err != nil { return false }
|
||||
if (st.Mode & syscall.S_IFMT) == syscall.S_IFCHR {
|
||||
var term string
|
||||
// i assume this fd is bound to the current terminal if it's a character device
|
||||
// if the assumption is wrong, you simply get extraneous ansi code in the output.
|
||||
term = os.Getenv("TERM")
|
||||
if term != "" && term != "dumb" { return true }
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
func NewAppLogger(id string, w io.Writer, mask haza.LogMask) *AppLogger {
|
||||
var l *AppLogger
|
||||
var f *os.File
|
||||
var ok bool
|
||||
var use_color bool
|
||||
|
||||
use_color = false
|
||||
f, ok = w.(*os.File)
|
||||
if ok { use_color = _is_ansi_tty(f.Fd()) }
|
||||
|
||||
l = &AppLogger{
|
||||
id: id,
|
||||
out: w,
|
||||
mask: mask,
|
||||
msg_chan: make(chan app_logger_msg_t, 256),
|
||||
use_color: use_color,
|
||||
}
|
||||
l.closed.Store(false)
|
||||
l.wg.Add(1)
|
||||
go l.logger_task()
|
||||
return l
|
||||
}
|
||||
|
||||
func NewAppLoggerToFile(id string, file_name string, max_size int64, rotate int, mask haza.LogMask) (*AppLogger, error) {
|
||||
var l *AppLogger
|
||||
var f *os.File
|
||||
var matched bool
|
||||
var err error
|
||||
|
||||
f, err = os.OpenFile(file_name, os.O_CREATE | os.O_APPEND | os.O_WRONLY, 0666)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
if os.PathSeparator == '/' {
|
||||
// this check is performed only on systems where the path separator is /.
|
||||
matched, _ = filepath.Match("/dev/*", file_name)
|
||||
if matched {
|
||||
// if the log file is under /dev, disable rotation
|
||||
max_size = 0
|
||||
rotate = 0
|
||||
}
|
||||
}
|
||||
|
||||
l = &AppLogger{
|
||||
id: id,
|
||||
out: f,
|
||||
mask: mask,
|
||||
file: f,
|
||||
file_name: file_name,
|
||||
file_max_size: max_size,
|
||||
file_rotate: rotate,
|
||||
msg_chan: make(chan app_logger_msg_t, 256),
|
||||
use_color: _is_ansi_tty(f.Fd()),
|
||||
}
|
||||
l.closed.Store(false)
|
||||
l.wg.Add(1)
|
||||
go l.logger_task()
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (l *AppLogger) Close() {
|
||||
if l.closed.CompareAndSwap(false, true) {
|
||||
l.msg_chan <- app_logger_msg_t{code: 1}
|
||||
l.wg.Wait()
|
||||
if l.file != nil { l.file.Close() }
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AppLogger) Rotate() {
|
||||
l.msg_chan <- app_logger_msg_t{code: 2}
|
||||
}
|
||||
|
||||
func (l *AppLogger) logger_task() {
|
||||
var msg app_logger_msg_t
|
||||
defer l.wg.Done()
|
||||
|
||||
main_loop:
|
||||
for {
|
||||
select {
|
||||
case msg = <-l.msg_chan:
|
||||
if msg.code == 0 {
|
||||
//l.out.Write([]byte(msg))
|
||||
io.WriteString(l.out, msg.data)
|
||||
if l.file_max_size > 0 && l.file != nil {
|
||||
var fi os.FileInfo
|
||||
var err error
|
||||
fi, err = l.file.Stat()
|
||||
if err == nil && fi.Size() >= l.file_max_size {
|
||||
l.rotate()
|
||||
}
|
||||
}
|
||||
} else if msg.code == 1 {
|
||||
break main_loop
|
||||
} else if msg.code == 2 {
|
||||
l.rotate()
|
||||
}
|
||||
// other code must not appear here.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *AppLogger) Write(id string, level haza.LogLevel, fmtstr string, args ...interface{}) {
|
||||
if l.mask & haza.LogMask(level) == 0 { return }
|
||||
l.write(id, level, 1, fmtstr, args...)
|
||||
}
|
||||
|
||||
func (l *AppLogger) WriteWithCallDepth(id string, level haza.LogLevel, call_depth int, fmtstr string, args ...interface{}) {
|
||||
if l.mask & haza.LogMask(level) == 0 { return }
|
||||
l.write(id, level, call_depth + 1, fmtstr, args...)
|
||||
}
|
||||
|
||||
func (l *AppLogger) write(id string, level haza.LogLevel, call_depth int, fmtstr string, args ...interface{}) {
|
||||
var now time.Time
|
||||
var off_m int
|
||||
var off_h int
|
||||
var off_s int
|
||||
var msg string
|
||||
var callerfile string
|
||||
var caller_line int
|
||||
var caller_ok bool
|
||||
var sb strings.Builder
|
||||
|
||||
//if l.mask & haza.LogMask(level) == 0 { return }
|
||||
|
||||
now = time.Now()
|
||||
|
||||
_, off_s = now.Zone()
|
||||
off_m = off_s / 60
|
||||
off_h = off_m / 60
|
||||
off_m = off_m % 60
|
||||
if off_m < 0 { off_m = -off_m }
|
||||
|
||||
sb.WriteString(
|
||||
fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d %+03d%02d ",
|
||||
now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second(), off_h, off_m))
|
||||
|
||||
_, callerfile, caller_line, caller_ok = runtime.Caller(1 + call_depth)
|
||||
|
||||
if caller_ok {
|
||||
sb.WriteString(fmt.Sprintf("[%s:%d] ", filepath.Base(callerfile), caller_line))
|
||||
}
|
||||
sb.WriteString(l.id)
|
||||
if id != "" {
|
||||
sb.WriteString("(")
|
||||
sb.WriteString(id)
|
||||
sb.WriteString(")")
|
||||
}
|
||||
sb.WriteString(": ")
|
||||
msg = fmt.Sprintf(fmtstr, args...)
|
||||
if (l.use_color) {
|
||||
var code string
|
||||
code = l.log_level_to_ansi_code(level)
|
||||
sb.WriteString(code)
|
||||
sb.WriteString(msg)
|
||||
if code != "" { sb.WriteString("\x1B[0m") }
|
||||
} else {
|
||||
sb.WriteString(msg)
|
||||
}
|
||||
if msg[len(msg) - 1] != '\n' { sb.WriteRune('\n') }
|
||||
|
||||
// use queue to avoid blocking operation as much as possible
|
||||
l.msg_chan <- app_logger_msg_t{ code: 0, data: sb.String() }
|
||||
}
|
||||
|
||||
func (l *AppLogger) rotate() {
|
||||
var f *os.File
|
||||
var fi os.FileInfo
|
||||
var i int
|
||||
var last_rot_no int
|
||||
var err error
|
||||
|
||||
if l.file == nil { return }
|
||||
if l.file_rotate <= 0 { return }
|
||||
|
||||
fi, err = l.file.Stat()
|
||||
if err == nil && fi.Size() <= 0 { return }
|
||||
|
||||
for i = l.file_rotate - 1; i > 0; i-- {
|
||||
if os.Rename(fmt.Sprintf("%s.%d", l.file_name, i), fmt.Sprintf("%s.%d", l.file_name, i + 1)) == nil {
|
||||
if last_rot_no == 0 { last_rot_no = i + 1 }
|
||||
}
|
||||
}
|
||||
if os.Rename(l.file_name, fmt.Sprintf("%s.%d", l.file_name, 1)) == nil {
|
||||
if last_rot_no == 0 { last_rot_no = 1 }
|
||||
}
|
||||
|
||||
f, err = os.OpenFile(l.file_name, os.O_CREATE | os.O_TRUNC | os.O_APPEND | os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
l.file.Close()
|
||||
l.file = nil
|
||||
l.out = os.Stderr
|
||||
// don't reset l.file_name. you can derive that there was an error
|
||||
// if l.file_name is not blank, and if l.out is os.Stderr,
|
||||
} else {
|
||||
l.file.Close()
|
||||
l.file = f
|
||||
l.out = l.file
|
||||
}
|
||||
}
|
||||
|
||||
func (l* AppLogger) log_level_to_ansi_code(level haza.LogLevel) string {
|
||||
switch level {
|
||||
case haza.LOG_ERROR:
|
||||
return "\x1B[31m" // red
|
||||
|
||||
case haza.LOG_WARN:
|
||||
return "\x1B[33m" // yellow
|
||||
|
||||
case haza.LOG_INFO:
|
||||
if (l.mask & haza.LogMask(haza.LOG_DEBUG)) != 0 {
|
||||
// if debug is enabled, change the color of info.
|
||||
// otherwisse no color
|
||||
return "\x1B[32m" // green
|
||||
}
|
||||
fallthrough
|
||||
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
59
cmd/main.go
Normal file
59
cmd/main.go
Normal file
@ -0,0 +1,59 @@
|
||||
package main
|
||||
|
||||
import "context"
|
||||
import "fmt"
|
||||
import "haza"
|
||||
import "net"
|
||||
import "os"
|
||||
//import "time"
|
||||
|
||||
func main() {
|
||||
var ds *haza.DhcpServer
|
||||
var logger *AppLogger
|
||||
var addr *net.UDPAddr
|
||||
var addr2 *net.UDPAddr
|
||||
var err error
|
||||
|
||||
// addr, err = net.ResolveUDPAddr("udp4", "0.0.0.0:10000")
|
||||
addr, err = net.ResolveUDPAddr("udp4", "192.168.1.130:10000")
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to resolve address - %s\n", err.Error())
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
addr2, err = net.ResolveUDPAddr("udp4", "192.168.1.114:10000")
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to resolve address - %s\n", err.Error())
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
logger = NewAppLogger("client", os.Stderr, haza.LOG_ALL)
|
||||
|
||||
ds, err = haza.NewDhcpServer(context.Background(), "haza-dhcpd", logger)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to create dhcp server - %s\n", err.Error())
|
||||
goto oops
|
||||
}
|
||||
|
||||
// consider this if you want some more freedom on supported systems
|
||||
// sysctl -w net.ipv4.ip_nonlocal_bind=1
|
||||
|
||||
//ds.AddListener("tun0", addr)
|
||||
err = ds.AddListener4("enp1s0", addr)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to add listgener for %v - %s\n", addr, err.Error())
|
||||
goto oops
|
||||
}
|
||||
err = ds.AddListener4("enp1s0", addr2)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to add listgener for %v - %s\n", addr2, err.Error())
|
||||
goto oops
|
||||
}
|
||||
|
||||
ds.StartService(nil)
|
||||
ds.WaitForTermination()
|
||||
os.Exit(0)
|
||||
|
||||
oops:
|
||||
os.Exit(-1)
|
||||
}
|
Reference in New Issue
Block a user