| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 | package logimport (	"io"	"os"	"path/filepath"	"sync"	"time")type Writer struct {	filePrefix string	fileSuffix string	path       string	date string	cur *os.File	mu  sync.Mutex}// NewRawWriter 新建日志写入接口// filePrefix 和 fileSuffix 分别作为文件名的前缀和后缀, path 为文件所存放的目录(e.g. /var/log)func NewRawWriter(filePrefix, fileSuffix, path string) (io.WriteCloser, error) {	if err := handlePath(path); err != nil {		return nil, err	}	w := new(Writer)	w.filePrefix = filePrefix	w.fileSuffix = fileSuffix	w.path = filepath.Join(path)	w.date = getDate()	w.cur = (*os.File)(nil)	return w, nil}// NewWriter 新建日志写入接口// per 和 fileSuffix 分别作为文件名的前缀和后缀, path 为文件所存放的目录(e.g. /var/log)// 与 NewRawWriter 不同: 通过 NewWriter 创建的文件会被缓存文件句柄. 对于通过 NewWriter 已创建的文件, 当再次通过 NewWriter 创建相同的// 文件时会返回之前已创建的句柄. 可以缓解 Too many open files 的问题func NewWriter(filePrefix, fileSuffix, path string) (io.WriteCloser, error) {	return _socketCache.Get(filePrefix, fileSuffix, path)}func (w *Writer) Write(p []byte) (n int, err error) {	if date := getDate(); date != w.date {		if err = w.Close(); err != nil {			return 0, err		}		w.date = date	}	if w.cur == nil {		if err = w.open(); err != nil {			return 0, err		}		return w.Write(p)	}	w.mu.Lock()	n, err = w.cur.Write(p)	w.mu.Unlock()	return}// Close 关闭 socketfunc (w *Writer) Close() error {	w.mu.Lock()	err := w.cur.Close()	w.cur = (*os.File)(nil)	w.mu.Unlock()	return err}func (w *Writer) open() error {	fi, err := os.OpenFile(w.curName(), os.O_WRONLY|os.O_CREATE|os.O_APPEND, os.ModePerm)	if err != nil {		return err	}	w.mu.Lock()	w.cur = fi	w.mu.Unlock()	return nil}func (w *Writer) curName() string {	return filepath.Join(w.path, w.filePrefix+"_"+w.date+w.fileSuffix)}func getDate() string {	return time.Now().Format("2006_01_02")}func handlePath(path string) error {	if _, err := os.Stat(path); err != nil {		if err = os.MkdirAll(path, os.ModePerm); err != nil {			return err		}		return err	}	return nil}// TODO 潜在的安全风险: 文件句柄会被保存在 map 中, 即使调用 Close 关闭文件句柄后 map 内依然会保存指针// TODO 随着程序长时间不间断运行, 因此会导致内存泄露. 但由于每日仅创建一个文件, 内存泄露在可控范围内. 因此 此问题暂不做处理type socketCache struct {	cache map[string]io.WriteCloser	mu    sync.Mutex}func (s *socketCache) Get(pre, suf, path string) (io.WriteCloser, error) {	s.mu.Lock()	defer s.mu.Unlock()	name := pre + suf + path	if cache, ok := s.cache[name]; ok {		return cache, nil	}	w, err := NewRawWriter(pre, suf, path)	if err != nil {		return nil, err	}	s.cache[name] = w	return w, nil}var (	_socketCache = socketCache{cache: make(map[string]io.WriteCloser)})
 |