| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 | 
							- package log
 
- import (
 
- 	"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 关闭 socket
 
- func (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)}
 
- )
 
 
  |