diff --git a/.gitignore b/.gitignore index b339b06..8b5b812 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ _testmain.go *.exe *.test config.json +*.log *.swp godns @@ -29,4 +30,4 @@ vendor/* /.idea /godns.iml /godns.ipr -/godns.iws \ No newline at end of file +/godns.iws diff --git a/dns_handler.go b/dns_handler.go index 10509ed..a8e6cd1 100644 --- a/dns_handler.go +++ b/dns_handler.go @@ -13,7 +13,7 @@ import ( "github.com/bitly/go-simplejson" ) -func get_currentIP(url string) (string, error) { +func getCurrentIP(url string) (string, error) { response, err := http.Get(url) if err != nil { @@ -27,13 +27,13 @@ func get_currentIP(url string) (string, error) { return string(body), nil } -func generate_header(content url.Values) url.Values { +func generateHeader(content url.Values) url.Values { header := url.Values{} - if(Configuration.LoginToken!=""){ - header.Add("login_token", Configuration.LoginToken) - }else{ - header.Add("login_email", Configuration.Email) - header.Add("login_password", Configuration.Password) + if configuration.LoginToken != "" { + header.Add("login_token", configuration.LoginToken) + } else { + header.Add("login_email", configuration.Email) + header.Add("login_password", configuration.Password) } header.Add("format", "json") header.Add("lang", "en") @@ -48,11 +48,11 @@ func generate_header(content url.Values) url.Values { return header } -func api_version() { - post_data("/Info.Version", nil) +func apiVersion() { + postData("/Info.Version", nil) } -func get_domain(name string) int64 { +func getDomain(name string) int64 { var ret int64 values := url.Values{} @@ -60,17 +60,17 @@ func get_domain(name string) int64 { values.Add("offset", "0") values.Add("length", "20") - response, err := post_data("/Domain.List", values) + response, err := postData("/Domain.List", values) if err != nil { log.Println("Failed to get domain list...") return -1 } - sjson, parse_err := simplejson.NewJson([]byte(response)) + sjson, parseErr := simplejson.NewJson([]byte(response)) - if parse_err != nil { - log.Println(parse_err) + if parseErr != nil { + log.Println(parseErr) return -1 } @@ -100,16 +100,16 @@ func get_domain(name string) int64 { return ret } -func get_subdomain(domain_id int64, name string) (string, string) { - log.Println("debug:", domain_id, name) +func getSubDomain(domainID int64, name string) (string, string) { + log.Println("debug:", domainID, name) var ret, ip string value := url.Values{} - value.Add("domain_id", strconv.FormatInt(domain_id, 10)) + value.Add("domain_id", strconv.FormatInt(domainID, 10)) value.Add("offset", "0") value.Add("length", "1") value.Add("sub_domain", name) - response, err := post_data("/Record.List", value) + response, err := postData("/Record.List", value) if err != nil { log.Println("Failed to get domain list") @@ -144,7 +144,7 @@ func get_subdomain(domain_id int64, name string) (string, string) { return ret, ip } -func update_ip(domain_id int64, sub_domain_id string, sub_domain_name string, ip string) { +func updateIP(domain_id int64, sub_domain_id string, sub_domain_name string, ip string) { value := url.Values{} value.Add("domain_id", strconv.FormatInt(domain_id, 10)) value.Add("record_id", sub_domain_id) @@ -153,7 +153,7 @@ func update_ip(domain_id int64, sub_domain_id string, sub_domain_name string, ip value.Add("record_line", "默认") value.Add("value", ip) - response, err := post_data("/Record.Modify", value) + response, err := postData("/Record.Modify", value) if err != nil { log.Println("Failed to update record to new IP!") @@ -174,13 +174,13 @@ func update_ip(domain_id int64, sub_domain_id string, sub_domain_name string, ip } -func post_data(url string, content url.Values) (string, error) { +func postData(url string, content url.Values) (string, error) { client := &http.Client{} - values := generate_header(content) + values := generateHeader(content) req, _ := http.NewRequest("POST", "https://dnsapi.cn"+url, strings.NewReader(values.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - req.Header.Set("User-Agent", fmt.Sprintf("GoDNS/0.1 (%s)", Configuration.Email)) + req.Header.Set("User-Agent", fmt.Sprintf("GoDNS/0.1 (%s)", configuration.Email)) response, err := client.Do(req) defer response.Body.Close() diff --git a/dns_handler_test.go b/dns_handler_test.go index d428a54..21f3ee1 100644 --- a/dns_handler_test.go +++ b/dns_handler_test.go @@ -4,8 +4,8 @@ import ( "testing" ) -func Test_get_current_IP(t *testing.T) { - ip, _ := get_currentIP("http://members.3322.org/dyndns/getip") +func testGetCurrentIP(t *testing.T) { + ip, _ := getCurrentIP("http://members.3322.org/dyndns/getip") if ip == "" { t.Log("IP is empty...") diff --git a/godns.go b/godns.go index a719004..2905432 100644 --- a/godns.go +++ b/godns.go @@ -16,7 +16,7 @@ const ( ) var ( - Configuration Settings + configuration Settings optConf = flag.String("c", "./config.json", "config file") optCommand = flag.String("s", "", "send signal to a master process: stop, quit, reopen, reload") optHelp = flag.Bool("h", false, "this help") @@ -35,7 +35,13 @@ func main() { } var err error - Configuration, err = LoadSettings(*optConf) + configuration, err = LoadSettings(*optConf) + + err = InitLogger(configuration.Log_Path, configuration.Log_Size, configuration.Log_Num) + if err != nil { + log.Println("InitLogger error:", err) + return + } if err != nil { fmt.Println(err.Error()) @@ -60,20 +66,20 @@ func dns_loop() { for { - domain_id := get_domain(Configuration.Domain) + domain_id := getDomain(configuration.Domain) if domain_id == -1 { continue } - currentIP, err := get_currentIP(Configuration.IP_Url) + currentIP, err := getCurrentIP(configuration.IP_Url) if err != nil { log.Println("get_currentIP:", err) continue } - sub_domain_id, ip := get_subdomain(domain_id, Configuration.Sub_domain) + sub_domain_id, ip := getSubDomain(domain_id, configuration.Sub_domain) if sub_domain_id == "" || ip == "" { log.Println("sub_domain:", sub_domain_id, ip) @@ -85,7 +91,7 @@ func dns_loop() { //Continue to check the IP of sub-domain if len(ip) > 0 && !strings.Contains(currentIP, ip) { log.Println("Start to update record IP...") - update_ip(domain_id, sub_domain_id, Configuration.Sub_domain, currentIP) + updateIP(domain_id, sub_domain_id, configuration.Sub_domain, currentIP) } else { log.Println("Current IP is same as domain IP, no need to update...") } diff --git a/logger.go b/logger.go new file mode 100644 index 0000000..a6474c1 --- /dev/null +++ b/logger.go @@ -0,0 +1,292 @@ +package main + +import ( + "bufio" + "bytes" + "errors" + "io" + "io/ioutil" + "log" + "os" + "runtime/debug" + "strconv" + "strings" + "sync" + "time" +) + +const ( + L_INFO int = iota + L_WARNING + L_DEBUG + PRE_INFO = "[ INFO]" + PRE_WARNING = "[WARNING]" + PRE_DEBUG = "[ DEBUG]" +) + +type Logger struct { + DEV_MODE bool + fd *os.File + size int + num int + level int + mu sync.Mutex + muSplit sync.Mutex + flushInterval int64 //Second + flushSize int + buf *bytes.Buffer + log *log.Logger +} + +func NewLogger(logfile string, size, num int, level int, flushInterval int64, flushSize int) (logger *Logger, err error) { + if size < 1 || num < 1 || level < L_INFO || len(logfile) < 1 { + err = errors.New("NewLogWriter:param error.") + return + } + logger = &Logger{size: size * 1024, num: num, level: level, DEV_MODE: false} + logger.fd, err = os.OpenFile(logfile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.ModeAppend|0666) + if err != nil { + logger = nil + return + } + log.SetOutput(logger) + if flushInterval > 0 && flushSize > 0 { + logger.buf = new(bytes.Buffer) + logger.log = log.New(logger.buf, "", log.LstdFlags) + + go func(interval int64, logger *Logger) { + defer func() { + if r := recover(); r != nil { + log.Printf("logger Tick, Recovered in %v:\n %s", r, debug.Stack()) + } + }() + c := time.Tick(time.Duration(interval) * time.Second) + for _ = range c { + logger.Flush() + } + }(flushInterval, logger) + } + return +} + +func InitLogger(logfile string, size, num int) (err error) { + logger, err := NewLogger(logfile, size, num, L_INFO, -1, -1) + if logger != nil { + logger.level = L_INFO - 1 + } + return +} + +//immplement write +func (this *Logger) Write(p []byte) (n int, err error) { + if this.DEV_MODE { + n, err = os.Stdout.Write(p) + return + } + n, err = this.fd.Write(p) + if err == nil { + fi, e := this.fd.Stat() + if e != nil { + err = e + return + } + if fi.Size() > int64(this.size) { + this.muSplit.Lock() + defer this.muSplit.Unlock() + + fname := fi.Name() + strings.HasSuffix(fname, ".log") + fbase := fname[:len(fname)-3] + + oldBs := make([]byte, 0, this.size) + newBs := []byte{} + fd, e := os.Open(fname) + if e != nil { + err = e + return + } + rd := bufio.NewReader(fd) + for { + line, e := rd.ReadBytes('\n') + if e == io.EOF { + break + } + if e != nil { + err = e + return + } + if len(oldBs)+len(line) > this.size { + newBs = append(newBs, line...) + } else { + oldBs = append(oldBs, line...) + } + } + fd.Close() + + _, err = this.saveLog(1, fbase, oldBs) + if err != nil { + return + } + err = this.fd.Close() + if err != nil { + return + } + err = os.Remove(fname) + if err != nil { + return + } + this.fd, err = os.OpenFile(fname, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.ModeAppend|0666) + if err != nil { + return + } + _, err = this.fd.Write(newBs) + if err != nil { + return + } + } + } + return +} + +func (this *Logger) saveLog(index int, fbase string, data []byte) (n int, err error) { + fn := fbase + strconv.Itoa(index) + ".log" + _, err = os.Stat(fn) + if index < this.num && err == nil { + var b []byte + b, err = ioutil.ReadFile(fn) + if err != nil { + return + } + n, err = this.saveLog(index+1, fbase, b) + if err != nil { + return + } + } + + fd, err := os.OpenFile(fn, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.ModePerm|0666) + if err != nil { + return + } + defer fd.Close() + n, err = fd.Write(data) + return +} + +//flush buf data to std log +func (this *Logger) Flush() { + if this.buf.Len() > 0 { + this.mu.Lock() + defer this.mu.Unlock() + + log.SetFlags(0) + log.Print(this.buf) + log.SetFlags(log.LstdFlags) + this.buf.Reset() + } +} + +//clean prefix and check buf size +func (this *Logger) clean() { + this.log.SetPrefix("") + if this.buf.Len()/1024 > this.flushSize { + go this.Flush() + } +} + +func (this *Logger) setPrefix(lv int) bool { + if lv > this.level { + return false + } + + switch lv { + case L_INFO: + this.log.SetPrefix(PRE_INFO) + case L_WARNING: + this.log.SetPrefix(PRE_WARNING) + case L_DEBUG: + this.log.SetPrefix(PRE_DEBUG) + default: + return false + } + return true +} + +func (this *Logger) logPrint(lv int, args ...interface{}) { + this.mu.Lock() + defer this.mu.Unlock() + + if !this.setPrefix(lv) { + return + } + this.log.Print(args...) + this.clean() +} + +func (this *Logger) logPrintln(lv int, args ...interface{}) { + this.mu.Lock() + defer this.mu.Unlock() + + if !this.setPrefix(lv) { + return + } + this.log.Println(args...) + this.clean() +} + +func (this *Logger) logPrintf(lv int, format string, args ...interface{}) { + this.mu.Lock() + defer this.mu.Unlock() + + if !this.setPrefix(lv) { + return + } + this.log.Printf(format, args...) + this.clean() +} + +//close fd +func (this *Logger) Close() { + if this.fd != nil { + this.Flush() + this.fd.Close() + } +} + +func (this *Logger) Info(args ...interface{}) { + this.logPrint(L_INFO, args...) +} + +func (this *Logger) Infoln(args ...interface{}) { + this.logPrintln(L_INFO, args...) +} + +func (this *Logger) Infof(format string, args ...interface{}) { + this.logPrintf(L_INFO, format, args...) +} + +func (this *Logger) Warning(args ...interface{}) { + this.logPrint(L_WARNING, args...) +} + +func (this *Logger) Warningln(args ...interface{}) { + this.logPrintln(L_WARNING, args...) +} + +func (this *Logger) Warningf(format string, args ...interface{}) { + this.logPrintf(L_WARNING, format, args...) +} + +func (this *Logger) Debug(args ...interface{}) { + this.logPrint(L_DEBUG, args...) + this.Flush() +} + +func (this *Logger) Debugln(args ...interface{}) { + this.logPrintln(L_DEBUG, args...) + this.Flush() +} + +func (this *Logger) Debugf(format string, args ...interface{}) { + this.logPrintf(L_DEBUG, format, args...) + this.Flush() +}