1、完善日志配置

2、文章自动生成摘要
3、修正文档名称问题
4、修复Redis无法读取缓存的BUG
pull/358/head
lifei6671 2018-07-25 14:46:56 +08:00
parent 774530bbc2
commit 1cbdd4baca
11 changed files with 226 additions and 73 deletions

40
cache/cache.go vendored
View File

@ -3,24 +3,56 @@ package cache
import ( import (
"github.com/astaxie/beego/cache" "github.com/astaxie/beego/cache"
"time" "time"
"encoding/gob"
"fmt"
"bytes"
"errors"
"github.com/astaxie/beego"
) )
var bm cache.Cache var bm cache.Cache
func Get(key string) interface{} { func Get(key string,e interface{}) error {
return bm.Get(key) val := bm.Get(key)
if val == nil {
return errors.New("cache does not exist")
}
if b,ok := val.([]byte); ok {
buf := bytes.NewBuffer(b)
decoder := gob.NewDecoder(buf)
err := decoder.Decode(e)
if err != nil {
fmt.Println("反序列化对象失败 ->", err)
}
return err
}
return errors.New("value is not []byte")
} }
func GetMulti(keys []string) []interface{} { func GetMulti(keys []string) []interface{} {
return bm.GetMulti(keys) return bm.GetMulti(keys)
} }
func Put(key string, val interface{}, timeout time.Duration) error { func Put(key string, val interface{}, timeout time.Duration) error {
return bm.Put(key, val, timeout) var buf bytes.Buffer
encoder := gob.NewEncoder(&buf)
err := encoder.Encode(val)
if err != nil {
beego.Error("序列化对象失败 ->",err)
return err
} }
return bm.Put(key, buf.String(), timeout)
}
func Delete(key string) error { func Delete(key string) error {
return bm.Delete(key) return bm.Delete(key)
} }

View File

@ -24,6 +24,7 @@ import (
"github.com/lifei6671/mindoc/conf" "github.com/lifei6671/mindoc/conf"
"github.com/lifei6671/mindoc/models" "github.com/lifei6671/mindoc/models"
"github.com/lifei6671/mindoc/utils/filetil" "github.com/lifei6671/mindoc/utils/filetil"
"github.com/astaxie/beego/cache/redis"
) )
// RegisterDataBase 注册数据库 // RegisterDataBase 注册数据库
@ -91,6 +92,8 @@ func RegisterModel() {
new(models.Label), new(models.Label),
new(models.Blog), new(models.Blog),
) )
gob.Register(models.Blog{})
gob.Register(models.Document{})
//migrate.RegisterMigration() //migrate.RegisterMigration()
} }
@ -100,28 +103,69 @@ func RegisterLogger(log string) {
logs.SetLogFuncCall(true) logs.SetLogFuncCall(true)
logs.SetLogger("console") logs.SetLogger("console")
logs.EnableFuncCallDepth(true) logs.EnableFuncCallDepth(true)
logs.Async()
if beego.AppConfig.DefaultBool("log_is_async", true) {
logs.Async(1e3)
}
if log == "" {
log = conf.WorkingDir("runtime","logs")
}
logPath := filepath.Join(log, "log.log") logPath := filepath.Join(log, "log.log")
if _, err := os.Stat(logPath); os.IsNotExist(err) { if _, err := os.Stat(log); os.IsNotExist(err) {
os.MkdirAll(log, 0777) os.MkdirAll(log, 0777)
}
if f, err := os.Create(logPath); err == nil {
f.Close()
config := make(map[string]interface{}, 1) config := make(map[string]interface{}, 1)
config["filename"] = logPath config["filename"] = logPath
config["perm"] = "0755"
config["rotate"] = true
b, _ := json.Marshal(config) if maxLines := beego.AppConfig.DefaultInt("log_maxlines", 1000000); maxLines > 0 {
config["maxLines"] = maxLines
beego.SetLogger("file", string(b)) }
if maxSize := beego.AppConfig.DefaultInt("log_maxsize", 1<<28); maxSize > 0 {
config["maxsize"] = maxSize
}
if !beego.AppConfig.DefaultBool("log_daily", true) {
config["daily"] = false
}
if maxDays := beego.AppConfig.DefaultInt("log_maxdays", 7); maxDays > 0 {
config["maxdays"] = maxDays
}
if level := beego.AppConfig.DefaultString("log_level", "Trace"); level != "" {
switch level {
case "Emergency":
config["level"] = beego.LevelEmergency;break
case "Alert":
config["level"] = beego.LevelAlert;break
case "Critical":
config["level"] = beego.LevelCritical;break
case "Error":
config["level"] = beego.LevelError; break
case "Warning":
config["level"] = beego.LevelWarning; break
case "Notice":
config["level"] = beego.LevelNotice; break
case "Informational":
config["level"] = beego.LevelInformational;break
case "Debug":
config["level"] = beego.LevelDebug;break
} }
} }
b, err := json.Marshal(config);
if err != nil {
beego.Error("初始化文件日志时出错 ->",err)
beego.SetLogger("file", `{"filename":"`+ logPath + `"}`)
}else{
beego.SetLogger(logs.AdapterFile, string(b))
}
beego.SetLogFuncCall(true) beego.SetLogFuncCall(true)
beego.BeeLogger.Async()
} }
// RunCommand 注册orm命令行工具 // RunCommand 注册orm命令行工具
@ -270,6 +314,10 @@ func RegisterCache() {
beegoCache.DefaultEvery = cacheInterval beegoCache.DefaultEvery = cacheInterval
cache.Init(memory) cache.Init(memory)
} else if cacheProvider == "redis" { } else if cacheProvider == "redis" {
//设置Redis前缀
if key := beego.AppConfig.DefaultString("cache_redis_prefix",""); key != "" {
redis.DefaultKey = key
}
var redisConfig struct { var redisConfig struct {
Conn string `json:"conn"` Conn string `json:"conn"`
Password string `json:"password"` Password string `json:"password"`
@ -320,7 +368,7 @@ func RegisterCache() {
} else { } else {
cache.Init(&cache.NullCache{}) cache.Init(&cache.NullCache{})
beego.Warn("不支持的缓存管道,缓存将禁用.") beego.Warn("不支持的缓存管道,缓存将禁用 ->" ,cacheProvider)
return return
} }
beego.Info("缓存初始化完成.") beego.Info("缓存初始化完成.")

View File

@ -26,6 +26,8 @@ sessionproviderconfig=./runtime/session
#时区设置 #时区设置
timezone = Asia/Shanghai timezone = Asia/Shanghai
####################MySQL 数据库配置########################### ####################MySQL 数据库配置###########################
#支持MySQL和sqlite3两种数据库如果是sqlite3 则 db_database 标识数据库的物理目录 #支持MySQL和sqlite3两种数据库如果是sqlite3 则 db_database 标识数据库的物理目录
db_adapter=mysql db_adapter=mysql
@ -87,12 +89,6 @@ export_queue_limit_num=100
#导出项目的缓存目录配置 #导出项目的缓存目录配置
export_output_path=./runtime/cache export_output_path=./runtime/cache
###############配置CDN加速##################
cdn=
cdnjs=
cdncss=
cdnimg=
################百度地图密钥################# ################百度地图密钥#################
baidumapkey= baidumapkey=
@ -116,35 +112,73 @@ ldap_user_role=2
#ldap搜索filter规则,AD服务器: objectClass=User, openldap服务器: objectClass=posixAccount ,也可以定义为其他属性,如: title=mindoc #ldap搜索filter规则,AD服务器: objectClass=User, openldap服务器: objectClass=posixAccount ,也可以定义为其他属性,如: title=mindoc
ldap_filter=objectClass=posixAccount ldap_filter=objectClass=posixAccount
###############配置CDN加速##################
cdn=
cdnjs=
cdncss=
cdnimg=
######################缓存配置############################### ######################缓存配置###############################
#是否开启缓存true 开启/false 不开启 #是否开启缓存true 开启/false 不开启
cache=false cache=false
#缓存方式:memory/memcache/redis/file #缓存方式:memory/memcache/redis/file
cache_provider=memory cache_provider=memory
#当配置缓存方式为memory时,内存回收时间,单位是秒 #当配置缓存方式为memory时,内存回收时间,单位是秒
cache_memory_interval=120 cache_memory_interval=120
#当缓存方式配置为file时,缓存的储存目录 #当缓存方式配置为file时,缓存的储存目录
cache_file_path=./runtime/cache/ cache_file_path=./runtime/cache/
#缓存文件后缀 #缓存文件后缀
cache_file_suffix=.bin cache_file_suffix=.bin
#文件缓存目录层级 #文件缓存目录层级
cache_file_dir_level=2 cache_file_dir_level=2
#文件缓存的默认过期时间 #文件缓存的默认过期时间
cache_file_expiry=3600 cache_file_expiry=3600
#memcache缓存服务器地址 #memcache缓存服务器地址
cache_memcache_host=127.0.0.1:11211 cache_memcache_host=127.0.0.1:11211
#redis服务器地址 #redis服务器地址
cache_redis_host=127.0.0.1:6379 cache_redis_host=127.0.0.1:6379
#redis数据库索引 #redis数据库索引
cache_redis_db=0 cache_redis_db=0
#redis服务器密码 #redis服务器密码
cache_redis_password= cache_redis_password=
#缓存键的前缀
cache_redis_prefix=mindoc::cache
#########日志储存配置##############
#日志保存路径在linux上自动创建的日志文件请不要删除否则将无法写入日志
log_path=./runtime/logs
#每个文件保存的最大行数,默认值 1000000
log_maxlines=1000000
# 每个文件保存的最大尺寸,默认值是 1 << 28, //256 MB
log_maxsize=
# 是否按照每天 logrotate默认是 true
log_daily=true
# 文件最多保存多少天,默认保存 7 天
log_maxdays=30
# 日志保存的时候的级别,默认是 Trace 级别,可选值: Emergency/Alert/Critical/Error/Warning/Notice/Informational/Debug/Trace
log_level=Trace
# 是否异步生成日志,默认是 true
log_is_async=true

View File

@ -15,6 +15,7 @@ import (
"github.com/astaxie/beego/orm" "github.com/astaxie/beego/orm"
"html/template" "html/template"
"encoding/json" "encoding/json"
"github.com/lifei6671/mindoc/utils"
) )
type BlogController struct{ type BlogController struct{
@ -58,6 +59,13 @@ func (c *BlogController) Index() {
c.Data["Model"] = blog c.Data["Model"] = blog
c.Data["Content"] = template.HTML(blog.BlogRelease) c.Data["Content"] = template.HTML(blog.BlogRelease)
if blog.BlogExcerpt == "" {
c.Data["Description"] = utils.AutoSummary(blog.BlogRelease,120)
}else{
c.Data["Description"] = blog.BlogExcerpt
}
if nextBlog,err := models.NewBlog().QueryNext(blogId);err == nil { if nextBlog,err := models.NewBlog().QueryNext(blogId);err == nil {
c.Data["Next"] = nextBlog c.Data["Next"] = nextBlog
} }
@ -86,6 +94,10 @@ func (c *BlogController) List() {
pager := pagination.NewPagination(c.Ctx.Request, totalCount, conf.PageSize, c.BaseUrl()) pager := pagination.NewPagination(c.Ctx.Request, totalCount, conf.PageSize, c.BaseUrl())
c.Data["PageHtml"] = pager.HtmlPages() c.Data["PageHtml"] = pager.HtmlPages()
for _,blog := range blogList { for _,blog := range blogList {
//如果没有添加文章摘要,则自动提取
if blog.BlogExcerpt == "" {
blog.BlogExcerpt = utils.AutoSummary(blog.BlogRelease,120)
}
blog.Link() blog.Link()
} }
} else { } else {

View File

@ -70,6 +70,7 @@ func (c *DocumentController) Index() {
c.Data["Title"] = doc.DocumentName c.Data["Title"] = doc.DocumentName
c.Data["Content"] = template.HTML(doc.Release) c.Data["Content"] = template.HTML(doc.Release)
c.Data["Description"] = utils.AutoSummary(doc.Release,120)
} }
}else { }else {
c.Data["Title"] = "概要" c.Data["Title"] = "概要"
@ -208,6 +209,10 @@ func (c *DocumentController) Read() {
c.Abort("500") c.Abort("500")
} }
c.Data["Description"] = utils.AutoSummary(doc.Release,120)
c.Data["Model"] = bookResult c.Data["Model"] = bookResult
c.Data["Result"] = template.HTML(tree) c.Data["Result"] = template.HTML(tree)
c.Data["Title"] = doc.DocumentName c.Data["Title"] = doc.DocumentName

View File

@ -510,8 +510,6 @@ func (this *Converter) convertToMobi() (err error) {
args := []string{ args := []string{
filepath.Join(this.OutputPath, "content.epub"), filepath.Join(this.OutputPath, "content.epub"),
filepath.Join(this.OutputPath, output, "book.mobi"), filepath.Join(this.OutputPath, output, "book.mobi"),
"--debug-pipeline",
"--verbose",
} }
cmd := exec.Command(ebookConvert, args...) cmd := exec.Command(ebookConvert, args...)
if this.Debug { if this.Debug {
@ -526,8 +524,6 @@ func (this *Converter) convertToPdf() (err error) {
args := []string{ args := []string{
filepath.Join(this.OutputPath, "content.epub"), filepath.Join(this.OutputPath, "content.epub"),
filepath.Join(this.OutputPath, output, "book.pdf"), filepath.Join(this.OutputPath, output, "book.pdf"),
"--debug-pipeline",
"--verbose",
} }
//页面大小 //页面大小
if len(this.Config.PaperSize) > 0 { if len(this.Config.PaperSize) > 0 {
@ -579,8 +575,6 @@ func (this *Converter) convertToDocx() (err error) {
args := []string{ args := []string{
filepath.Join(this.OutputPath , "content.epub"), filepath.Join(this.OutputPath , "content.epub"),
filepath.Join(this.OutputPath , output , "book.docx"), filepath.Join(this.OutputPath , output , "book.docx"),
"--debug-pipeline",
"--verbose",
} }
args = append(args, "--docx-no-toc") args = append(args, "--docx-no-toc")

View File

@ -103,18 +103,18 @@ func (b *Blog) Find(blogId int) (*Blog,error) {
//从缓存中读取文章 //从缓存中读取文章
func (b *Blog) FindFromCache(blogId int) (blog *Blog,err error) { func (b *Blog) FindFromCache(blogId int) (blog *Blog,err error) {
key := fmt.Sprintf("blog-id-%d",blogId); key := fmt.Sprintf("blog-id-%d",blogId);
obj := cache.Get(key) var temp Blog
if err := cache.Get(key,&temp); err == nil {
if b,ok := obj.(Blog); ok { b = &temp
blog = &b b.Link()
blog.Link()
beego.Info("从缓存读取文章成功 ->", key) beego.Info("从缓存读取文章成功 ->", key)
return return b,nil
} }
blog,err = b.Find(blogId) blog,err = b.Find(blogId)
if err == nil { if err == nil {
//默认一个小时 //默认一个小时
if err := cache.Put(key,*blog,time.Hour * 1); err != nil { if err := cache.Put(key,blog,time.Hour * 1); err != nil {
beego.Error("将文章存入缓存失败 ->",err) beego.Error("将文章存入缓存失败 ->",err)
} }
} }
@ -219,7 +219,7 @@ func (b *Blog) Save(cols ...string) error {
if b.BlogId > 0 { if b.BlogId > 0 {
b.Modified = time.Now() b.Modified = time.Now()
_,err = o.Update(b,cols...) _,err = o.Update(b,cols...)
key := fmt.Sprintf("blog-id-%d",b.BlogId); key := fmt.Sprintf("blog-id-%d", b.BlogId )
cache.Delete(key) cache.Delete(key)
}else{ }else{
@ -320,7 +320,7 @@ func (b *Blog) QueryNext(blogId int) (*Blog,error) {
err := o.QueryTable(b.TableNameWithPrefix()).Filter("order_index__gte",blog.OrderIndex).Filter("blog_id__gt",blogId).OrderBy("-order_index","-blog_id").One(blog) err := o.QueryTable(b.TableNameWithPrefix()).Filter("order_index__gte",blog.OrderIndex).Filter("blog_id__gt",blogId).OrderBy("-order_index","-blog_id").One(blog)
if err != nil { if err != nil && err != orm.ErrNoRows{
beego.Error("查询文章时出错 ->",err) beego.Error("查询文章时出错 ->",err)
} }
return blog,err return blog,err
@ -338,7 +338,7 @@ func (b *Blog) QueryPrevious(blogId int) (*Blog,error) {
err := o.QueryTable(b.TableNameWithPrefix()).Filter("order_index__lte",blog.OrderIndex).Filter("blog_id__lt",blogId).OrderBy("-order_index","-blog_id").One(blog) err := o.QueryTable(b.TableNameWithPrefix()).Filter("order_index__lte",blog.OrderIndex).Filter("blog_id__lt",blogId).OrderBy("-order_index","-blog_id").One(blog)
if err != nil { if err != nil && err != orm.ErrNoRows{
beego.Error("查询文章时出错 ->",err) beego.Error("查询文章时出错 ->",err)
} }
return blog,err return blog,err
@ -353,13 +353,13 @@ func (b *Blog) LinkAttach() (err error) {
//当不是关联文章时用文章ID去查询附件 //当不是关联文章时用文章ID去查询附件
if b.BlogType != 1 || b.DocumentId <= 0 { if b.BlogType != 1 || b.DocumentId <= 0 {
_, err = o.QueryTable(NewAttachment().TableNameWithPrefix()).Filter("document_id", b.BlogId).Filter("book_id",0).All(&attachList) _, err = o.QueryTable(NewAttachment().TableNameWithPrefix()).Filter("document_id", b.BlogId).Filter("book_id",0).All(&attachList)
if err != nil { if err != nil && err != orm.ErrNoRows{
beego.Error("查询文章附件时出错 ->", err) beego.Error("查询文章附件时出错 ->", err)
} }
}else { }else {
_, err = o.QueryTable(NewAttachment().TableNameWithPrefix()).Filter("document_id", b.DocumentId).Filter("book_id", b.BookId).All(&attachList) _, err = o.QueryTable(NewAttachment().TableNameWithPrefix()).Filter("document_id", b.DocumentId).Filter("book_id", b.BookId).All(&attachList)
if err != nil { if err != nil && err != orm.ErrNoRows{
beego.Error("查询文章附件时出错 ->", err) beego.Error("查询文章附件时出错 ->", err)
} }
} }

View File

@ -3,7 +3,6 @@ package models
import ( import (
"time" "time"
"encoding/json"
"fmt" "fmt"
"strconv" "strconv"
@ -151,18 +150,18 @@ func (m *Document) RecursiveDocument(docId int) error {
//将文档写入缓存 //将文档写入缓存
func (m *Document) PutToCache() { func (m *Document) PutToCache() {
go func(m Document) { go func(m Document) {
if v, err := json.Marshal(&m); err == nil {
if m.Identify == "" { if m.Identify == "" {
if err := cache.Put("Document.Id."+strconv.Itoa(m.DocumentId), v, time.Second*3600); err != nil { if err := cache.Put("Document.Id."+strconv.Itoa(m.DocumentId), m, time.Second*3600); err != nil {
beego.Info("文档缓存失败:", m.DocumentId) beego.Info("文档缓存失败:", m.DocumentId)
} }
} else { } else {
if err := cache.Put(fmt.Sprintf("Document.BookId.%d.Identify.%s", m.BookId, m.Identify), v, time.Second*3600); err != nil { if err := cache.Put(fmt.Sprintf("Document.BookId.%d.Identify.%s", m.BookId, m.Identify), m, time.Second*3600); err != nil {
beego.Info("文档缓存失败:", m.DocumentId) beego.Info("文档缓存失败:", m.DocumentId)
} }
} }
}
}(*m) }(*m)
} }
@ -179,31 +178,35 @@ func (m *Document) RemoveCache() {
//从缓存获取 //从缓存获取
func (m *Document) FromCacheById(id int) (*Document, error) { func (m *Document) FromCacheById(id int) (*Document, error) {
b := cache.Get("Document.Id." + strconv.Itoa(id))
if v, ok := b.([]byte); ok {
if err := json.Unmarshal(v, m); err == nil { var doc Document
if err := cache.Get("Document.Id."+strconv.Itoa(id), &m); err == nil {
m = &doc
beego.Info("从缓存中获取文档信息成功", m.DocumentId) beego.Info("从缓存中获取文档信息成功", m.DocumentId)
return m, nil return m, nil
} }
}
defer func() {
if m.DocumentId > 0 { if m.DocumentId > 0 {
m.PutToCache() m.PutToCache()
} }
}() m,err := m.Find(id)
return m.Find(id)
if err == nil {
m.PutToCache()
}
return m,err
} }
//根据文档标识从缓存中查询文档 //根据文档标识从缓存中查询文档
func (m *Document) FromCacheByIdentify(identify string, bookId int) (*Document, error) { func (m *Document) FromCacheByIdentify(identify string, bookId int) (*Document, error) {
b := cache.Get(fmt.Sprintf("Document.BookId.%d.Identify.%s", bookId, identify))
if v, ok := b.([]byte); ok { key := fmt.Sprintf("Document.BookId.%d.Identify.%s", bookId, identify)
if err := json.Unmarshal(v, m); err == nil {
beego.Info("从缓存中获取文档信息成功", m.DocumentId, identify) if err := cache.Get(key,m); err == nil {
beego.Info("从缓存中获取文档信息成功", key)
return m, nil return m, nil
} }
}
defer func() { defer func() {
if m.DocumentId > 0 { if m.DocumentId > 0 {
m.PutToCache() m.PutToCache()

View File

@ -29,3 +29,28 @@ func StripTags(s string) string {
return src return src
} }
//自动提取文章摘要
func AutoSummary(body string,l int) string {
//匹配图片,如果图片语法是在代码块中,这里同样会处理
re := regexp.MustCompile(`<p>(.*?)</p>`)
contents := re.FindAllString(body, -1)
if len(contents) <= 0 {
return ""
}
content := ""
for _,s := range contents {
b := strings.Replace(StripTags(s),"\n","", -1)
if l <= 0 {
break
}
l = l - len([]rune(b))
content += b
}
return content
}

View File

@ -8,7 +8,7 @@
<meta name="author" content="Minho" /> <meta name="author" content="Minho" />
<meta name="site" content="https://www.iminho.me" /> <meta name="site" content="https://www.iminho.me" />
<meta name="keywords" content="{{.Model.BlogTitle}}"> <meta name="keywords" content="{{.Model.BlogTitle}}">
<meta name="description" content="{{.Model.BlogTitle}}-{{.Model.BlogExcerpt}}"> <meta name="description" content="{{.Model.BlogTitle}}-{{.Description}}">
<title>{{.Model.BlogTitle}} - Powered by MinDoc</title> <title>{{.Model.BlogTitle}} - Powered by MinDoc</title>
<!-- Bootstrap --> <!-- Bootstrap -->

View File

@ -11,7 +11,7 @@
<meta name="author" content="Minho" /> <meta name="author" content="Minho" />
<meta name="site" content="https://www.iminho.me" /> <meta name="site" content="https://www.iminho.me" />
<meta name="keywords" content="{{.Model.BookName}},{{.Title}}"> <meta name="keywords" content="{{.Model.BookName}},{{.Title}}">
<meta name="description" content="{{.Title}}-{{.Model.Description}}"> <meta name="description" content="{{.Title}}-{{if .Description}}{{.Description}}{{else}}{{.Model.Description}}{{end}}">
<!-- Bootstrap --> <!-- Bootstrap -->
<link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet"> <link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet">