Compare commits

..
12 Commits
Author SHA1 Message Date
Umputun 440beb3303 make sure last returns [] list #262 2019-01-25 23:20:30 -06:00
Umputun f7de26ef4b enforec empty list of comments on nil response 2019-01-25 22:49:52 -06:00
Umputun 6250f2e52e allow empty list of comments on find for fresh post without anything #262 2019-01-25 18:24:58 -06:00
Umputun 1cc09a33f6 fix lack of counter invalidation from the cache #261 2019-01-25 17:35:38 -06:00
Umputun 5a4e84550d Merge branch 'master' of github.com:umputun/remark 2019-01-25 17:10:26 -06:00
Umputun 8f0b6a6a59 add unit test for counters #261 2019-01-25 17:10:22 -06:00
Misha VyrtsevandUmputun f4a616e2de add widgets to demo page (#263)
* add widgets to demo page

* fix border color in dark mode

* demo page adjusting

* fix indentation
2019-01-25 16:43:57 -06:00
Umputun 1475060555 revendore with auth 0.4.1, fix yandex auth provider bug 2019-01-24 13:58:54 -06:00
Umputun 1761995482 skip logger pkg 2019-01-22 23:21:36 -06:00
Umputun 7aeec9535f revendor with fixed lgr pkg option 2019-01-22 18:44:59 -06:00
Umputun 32503395ea add caller pkg to log 2019-01-22 18:30:22 -06:00
Pavel AlimpievandUmputun e82a8f9824 Update README.md (#259) 2019-01-22 11:12:42 -06:00
20 changed files with 255 additions and 49 deletions
+1 -1
View File
@@ -266,7 +266,7 @@ Admins/moderators should be defined in `docker-compose.yml` as a list of user ID
```
environment:
- ADMIN=github_ef0f706a79cc24b17bbbb374cd234a691a034128,github_dae9983158e9e5e127ef2b87a411ef13c891e9e5
- ADMIN_SHARED_ID=github_ef0f706a79cc24b17bbbb374cd234a691a034128,github_dae9983158e9e5e127ef2b87a411ef13c891e9e5
```
To get user id just login and click on your username or any other user you want to promote to admins.
+6 -6
View File
@@ -112,7 +112,7 @@
version = "v1.0.0"
[[projects]]
digest = "1:d31c72099b9e195785933d11441bc92cce9b2c421f8fca712a7eaf36a8e55a47"
digest = "1:38940db39a8ee330d3e7f68ab4c298575462f5986ae2bb0cafa86f095e9d32ad"
name = "github.com/go-pkgz/auth"
packages = [
".",
@@ -123,16 +123,16 @@
"token",
]
pruneopts = "UT"
revision = "92256b685196d26a346dfbedea12898fe589b0eb"
version = "v0.4.0"
revision = "7597c083287c33ba5362687a7815a032fd92c418"
version = "v0.4.1"
[[projects]]
digest = "1:1933dabfb0e07548ed9684fdef857cc6f2413bbbb74ba838a7c1f77407b8fd90"
digest = "1:7b1f422f560b103f435f8501f854f238f1ce00d0f8146e76eacdfa71f2c8d8c0"
name = "github.com/go-pkgz/lgr"
packages = ["."]
pruneopts = "UT"
revision = "8f56fccc1a15dfe9c7f72e049fc87cc378205b97"
version = "v0.2.2"
revision = "dbd940fec9650260d15aa071cab658d0502ed92f"
version = "v0.3.2"
[[projects]]
digest = "1:c509e3f646c48148f2239fa66d5cd62785dfd057b5d2b46972e45b60049c19ed"
+1 -1
View File
@@ -65,7 +65,7 @@ func setupLog(dbg bool) {
log.Setup(log.Debug, log.CallerFile, log.Msec, log.LevelBraces)
return
}
log.Setup(log.Msec, log.LevelBraces)
log.Setup(log.Msec, log.LevelBraces, log.CallerPkg, log.CallerIgnore("logger"))
}
// getDump reads runtime stack and returns as a string
+1 -1
View File
@@ -58,7 +58,7 @@ func (a *admin) deleteCommentCtrl(w http.ResponseWriter, r *http.Request) {
rest.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't delete comment")
return
}
a.cache.Flush(cache.Flusher(locator.SiteID).Scopes(locator.URL, lastCommentsScope))
a.cache.Flush(cache.Flusher(locator.SiteID).Scopes(locator.SiteID, locator.URL, lastCommentsScope))
render.Status(r, http.StatusOK)
render.JSON(w, r, R.JSON{"id": id, "locator": locator})
}
+81 -2
View File
@@ -16,12 +16,13 @@ import (
jwt "github.com/dgrijalva/jwt-go"
"github.com/go-pkgz/auth/token"
R "github.com/go-pkgz/rest"
"github.com/umputun/remark/backend/app/store/service"
"github.com/go-pkgz/rest/cache"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/umputun/remark/backend/app/store"
"github.com/umputun/remark/backend/app/store/service"
)
func TestAdmin_Delete(t *testing.T) {
@@ -36,12 +37,33 @@ func TestAdmin_Delete(t *testing.T) {
id1 := addComment(t, c1, ts)
addComment(t, c2, ts)
// check last comments
res, code := get(t, ts.URL+"/api/v1/last/2?site=radio-t")
assert.Equal(t, 200, code)
comments := []store.Comment{}
err := json.Unmarshal([]byte(res), &comments)
assert.Nil(t, err)
assert.Equal(t, 2, len(comments), "should have 2 comments")
// check multi count
resp, err := post(t, ts.URL+"/api/v1/counts?site=radio-t", `["https://radio-t.com/blah","https://radio-t.com/blah2"]`)
assert.Nil(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
bb, err := ioutil.ReadAll(resp.Body)
assert.Nil(t, err)
j := []store.PostInfo{}
err = json.Unmarshal(bb, &j)
assert.Nil(t, err)
assert.Equal(t, []store.PostInfo([]store.PostInfo{{URL: "https://radio-t.com/blah", Count: 2},
{URL: "https://radio-t.com/blah2", Count: 0}}), j)
// delete a comment
client := http.Client{}
req, err := http.NewRequest(http.MethodDelete,
fmt.Sprintf("%s/api/v1/admin/comment/%s?site=radio-t&url=https://radio-t.com/blah", ts.URL, id1), nil)
assert.Nil(t, err)
req.SetBasicAuth("admin", "password")
resp, err := client.Do(req)
resp, err = client.Do(req)
assert.Nil(t, err)
assert.Equal(t, 200, resp.StatusCode)
@@ -52,6 +74,35 @@ func TestAdmin_Delete(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, "", cr.Text)
assert.True(t, cr.Deleted)
// check last comments updated
res, code = get(t, ts.URL+"/api/v1/last/2?site=radio-t")
assert.Equal(t, 200, code)
comments = []store.Comment{}
err = json.Unmarshal([]byte(res), &comments)
assert.Nil(t, err)
assert.Equal(t, 1, len(comments), "should have 1 comments")
// check count updated
res, code = get(t, ts.URL+"/api/v1/count?site=radio-t&url=https://radio-t.com/blah")
assert.Equal(t, 200, code)
b := map[string]interface{}{}
err = json.Unmarshal([]byte(res), &b)
assert.Nil(t, err)
t.Logf("%#v", b)
assert.Equal(t, 1.0, b["count"], "should report 1 comments")
// check multi count updated
resp, err = post(t, ts.URL+"/api/v1/counts?site=radio-t", `["https://radio-t.com/blah","https://radio-t.com/blah2"]`)
assert.Nil(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
bb, err = ioutil.ReadAll(resp.Body)
assert.Nil(t, err)
j = []store.PostInfo{}
err = json.Unmarshal(bb, &j)
assert.Nil(t, err)
assert.Equal(t, []store.PostInfo([]store.PostInfo{{URL: "https://radio-t.com/blah", Count: 1},
{URL: "https://radio-t.com/blah2", Count: 0}}), j)
}
func TestAdmin_Title(t *testing.T) {
@@ -262,6 +313,7 @@ func TestAdmin_Block(t *testing.T) {
assert.Equal(t, "", comments.Comments[0].Text)
assert.True(t, comments.Comments[0].Deleted)
srv.Cache = &cache.Nop{} // TODO: with lru cache it won't be refreshed and invalidated for long time
time.Sleep(50 * time.Millisecond)
res, code = get(t, ts.URL+"/api/v1/find?site=radio-t&url=https://radio-t.com/blah&sort=+time")
assert.Equal(t, 200, code)
@@ -392,6 +444,33 @@ func TestAdmin_ReadOnly(t *testing.T) {
assert.Equal(t, http.StatusCreated, resp.StatusCode)
}
func TestAdmin_ReadOnlyNoComments(t *testing.T) {
ts, srv, teardown := startupT(t)
defer teardown()
client := http.Client{}
// set post to read-only
req, err := http.NewRequest(http.MethodPut,
fmt.Sprintf("%s/api/v1/admin/readonly?site=radio-t&url=https://radio-t.com/blah&ro=1", ts.URL), nil)
assert.Nil(t, err)
req.SetBasicAuth("admin", "password")
resp, err := client.Do(req)
require.Nil(t, err)
assert.Equal(t, 200, resp.StatusCode)
_, err = srv.DataService.Info(store.Locator{SiteID: "radio-t", URL: "https://radio-t.com/blah"}, 0)
assert.NotNil(t, err)
res, code := get(t, ts.URL+"/api/v1/find?site=radio-t&url=https://radio-t.com/blah&format=tree")
assert.Equal(t, 200, code)
comments := commentsWithInfo{}
err = json.Unmarshal([]byte(res), &comments)
assert.Nil(t, err)
assert.Equal(t, 0, len(comments.Comments), "should have 0 comments")
assert.True(t, comments.Info.ReadOnly)
t.Logf("%+v", comments)
}
func TestAdmin_ReadOnlyWithAge(t *testing.T) {
ts, srv, teardown := startupT(t)
defer teardown()
+2 -1
View File
@@ -326,7 +326,8 @@ func encodeJSONWithHTML(v interface{}) ([]byte, error) {
return buf.Bytes(), nil
}
func filterComments(comments []store.Comment, fn func(c store.Comment) bool) (filtered []store.Comment) {
func filterComments(comments []store.Comment, fn func(c store.Comment) bool) []store.Comment {
filtered := []store.Comment{}
for _, c := range comments {
if fn(c) {
filtered = append(filtered, c)
+1 -1
View File
@@ -134,7 +134,7 @@ func (s *Rest) updateCommentCtrl(w http.ResponseWriter, r *http.Request) {
return
}
s.Cache.Flush(cache.Flusher(locator.SiteID).Scopes(locator.URL, lastCommentsScope, user.ID))
s.Cache.Flush(cache.Flusher(locator.SiteID).Scopes(locator.SiteID, locator.URL, lastCommentsScope, user.ID))
render.JSON(w, r, res)
}
+29 -4
View File
@@ -216,21 +216,34 @@ func TestRest_UpdateDelete(t *testing.T) {
Locator: store.Locator{SiteID: "radio-t", URL: "https://radio-t.com/blah1"}}
id := addComment(t, c1, ts)
// check multi count updated
resp, err := post(t, ts.URL+"/api/v1/counts?site=radio-t", `["https://radio-t.com/blah1","https://radio-t.com/blah2"]`)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
bb, err := ioutil.ReadAll(resp.Body)
require.NoError(t, err)
j := []store.PostInfo{}
err = json.Unmarshal(bb, &j)
assert.Nil(t, err)
assert.Equal(t, []store.PostInfo([]store.PostInfo{{URL: "https://radio-t.com/blah1", Count: 1},
{URL: "https://radio-t.com/blah2", Count: 0}}), j)
// delete a comment
client := http.Client{}
req, err := http.NewRequest(http.MethodPut, ts.URL+"/api/v1/comment/"+id+"?site=radio-t&url=https://radio-t.com/blah1",
strings.NewReader(`{"delete": true, "summary":"removed by user"}`))
assert.Nil(t, err)
require.NoError(t, err)
req.Header.Add("X-JWT", devToken)
b, err := client.Do(req)
assert.Nil(t, err)
require.NoError(t, err)
body, err := ioutil.ReadAll(b.Body)
assert.Nil(t, err)
require.NoError(t, err)
assert.Equal(t, 200, b.StatusCode, string(body))
// comments returned by update
c2 := store.Comment{}
err = json.Unmarshal(body, &c2)
assert.Nil(t, err)
require.NoError(t, err)
assert.Equal(t, id, c2.ID)
assert.True(t, c2.Deleted)
@@ -243,6 +256,18 @@ func TestRest_UpdateDelete(t *testing.T) {
assert.Equal(t, "", c3.Text)
assert.Equal(t, "", c3.Orig)
assert.True(t, c3.Deleted)
// check multi count updated
resp, err = post(t, ts.URL+"/api/v1/counts?site=radio-t", `["https://radio-t.com/blah1","https://radio-t.com/blah2"]`)
assert.Nil(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
bb, err = ioutil.ReadAll(resp.Body)
assert.Nil(t, err)
j = []store.PostInfo{}
err = json.Unmarshal(bb, &j)
require.NoError(t, err)
assert.Equal(t, []store.PostInfo([]store.PostInfo{{URL: "https://radio-t.com/blah1", Count: 0},
{URL: "https://radio-t.com/blah2", Count: 0}}), j)
}
func TestRest_UpdateNotOwner(t *testing.T) {
+4 -1
View File
@@ -31,13 +31,16 @@ func (s *Rest) findCommentsCtrl(w http.ResponseWriter, r *http.Request) {
data, err := s.Cache.Get(key, func() ([]byte, error) {
comments, e := s.DataService.Find(locator, sort)
if e != nil {
return nil, e
comments = []store.Comment{} // error should clear comments and continue for post info
}
maskedComments := s.adminService.alterComments(comments, r)
var b []byte
switch r.URL.Query().Get("format") {
case "tree":
tree := rest.MakeTree(maskedComments, sort, s.ReadOnlyAge)
if tree.Nodes == nil { // eliminate json nil serialization
tree.Nodes = []*rest.Node{}
}
if s.DataService.IsReadOnly(locator) {
tree.Info.ReadOnly = true
}
+16 -6
View File
@@ -10,6 +10,7 @@ import (
"time"
R "github.com/go-pkgz/rest"
"github.com/go-pkgz/rest/cache"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -69,8 +70,12 @@ func TestRest_Find(t *testing.T) {
ts, _, teardown := startupT(t)
defer teardown()
_, code := get(t, ts.URL+"/api/v1/find?site=radio-t&url=https://radio-t.com/blah1")
assert.Equal(t, 400, code, "nothing in")
res, code := get(t, ts.URL+"/api/v1/find?site=radio-t&url=https://radio-t.com/blah1")
assert.Equal(t, 200, code)
comments := commentsWithInfo{}
err := json.Unmarshal([]byte(res), &comments)
assert.Nil(t, err)
assert.Equal(t, 0, len(comments.Comments), "should have 0 comments")
c1 := store.Comment{Text: "test test #1", ParentID: "",
Locator: store.Locator{SiteID: "radio-t", URL: "https://radio-t.com/blah1"}}
@@ -83,10 +88,10 @@ func TestRest_Find(t *testing.T) {
assert.NotEqual(t, id1, id2)
// get sorted by +time
res, code := get(t, ts.URL+"/api/v1/find?site=radio-t&url=https://radio-t.com/blah1&sort=+time")
res, code = get(t, ts.URL+"/api/v1/find?site=radio-t&url=https://radio-t.com/blah1&sort=+time")
assert.Equal(t, 200, code)
comments := commentsWithInfo{}
err := json.Unmarshal([]byte(res), &comments)
comments = commentsWithInfo{}
err = json.Unmarshal([]byte(res), &comments)
assert.Nil(t, err)
assert.Equal(t, 2, len(comments.Comments), "should have 2 comments")
assert.Equal(t, id1, comments.Comments[0].ID)
@@ -194,6 +199,10 @@ func TestRest_Last(t *testing.T) {
ts, srv, teardown := startupT(t)
defer teardown()
res, code := get(t, ts.URL+"/api/v1/last/2?site=radio-t")
assert.Equal(t, 200, code)
assert.Equal(t, "[]\n", res, "empty last should return empty list")
c1 := store.Comment{Text: "test test #1", ParentID: "p1",
Locator: store.Locator{SiteID: "radio-t", URL: "https://radio-t.com/blah1"}}
c2 := store.Comment{Text: "test test #2", ParentID: "p1",
@@ -204,7 +213,7 @@ func TestRest_Last(t *testing.T) {
id1 := addComment(t, c1, ts)
id2 := addComment(t, c2, ts)
res, code := get(t, ts.URL+"/api/v1/last/2?site=radio-t")
res, code = get(t, ts.URL+"/api/v1/last/2?site=radio-t")
assert.Equal(t, 200, code)
comments := []store.Comment{}
err := json.Unmarshal([]byte(res), &comments)
@@ -227,6 +236,7 @@ func TestRest_Last(t *testing.T) {
err = srv.DataService.Delete(store.Locator{SiteID: "radio-t", URL: "https://radio-t.com/blah1"}, id1, store.SoftDelete)
assert.Nil(t, err)
srv.Cache.Flush(cache.FlusherRequest{})
res, code = get(t, ts.URL+"/api/v1/last/5?site=radio-t")
assert.Equal(t, 200, code)
err = json.Unmarshal([]byte(res), &comments)
+4 -1
View File
@@ -179,6 +179,9 @@ func startupT(t *testing.T) (ts *httptest.Server, srv *Rest, teardown func()) {
b, err := engine.NewBoltDB(bolt.Options{}, engine.BoltSite{FileName: testDb, SiteID: "radio-t"})
require.Nil(t, err)
memCache, err := cache.NewMemoryCache()
assert.NoError(t, err)
adminStore := adminstore.NewStaticStore("123456", []string{"a1", "a2"}, "admin@remark-42.com")
restrictedWordsMatcher := service.NewRestrictedWordsMatcher(service.StaticRestrictedWordsLister{Words: []string{"duck"}})
@@ -198,7 +201,7 @@ func startupT(t *testing.T) (ts *httptest.Server, srv *Rest, teardown func()) {
SecretReader: token.SecretFunc(func() (string, error) { return "secret", nil }),
AvatarStore: avatar.NewLocalFS("/tmp/ava-remark42"),
}),
Cache: &cache.Nop{},
Cache: memCache,
WebRoot: "/tmp",
RemarkURL: "https://demo.remark42.com",
+2 -2
View File
@@ -111,8 +111,8 @@ func (t *Tree) proc(comments []store.Comment, node *Node, rd *recurData, parentI
}
// filter returns comments for parentID
func (t *Tree) filter(comments []store.Comment, fn func(comment store.Comment) bool) (f []store.Comment) {
func (t *Tree) filter(comments []store.Comment, fn func(comment store.Comment) bool) []store.Comment {
f := []store.Comment{}
for _, c := range comments {
if fn(c) {
f = append(f, c)
@@ -170,6 +170,8 @@ func (b *BoltDB) Find(locator store.Locator, sortFld string) (comments []store.C
// Last returns up to max last comments for given siteID
func (b *BoltDB) Last(siteID string, max int) (comments []store.Comment, err error) {
comments = []store.Comment{}
if max > lastLimit || max == 0 {
max = lastLimit
}
@@ -20,6 +20,10 @@ func TestBoltAdmin_Delete(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, 2, len(res), "initially 2 comments")
count, err := b.Count(loc)
require.NoError(t, err)
assert.Equal(t, 2, count, "count=2 initially")
err = b.Delete(loc, res[0].ID, store.SoftDelete)
assert.Nil(t, err)
@@ -37,6 +41,10 @@ func TestBoltAdmin_Delete(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, 1, len(comments), "1 in last, 1 removed")
count, err = b.Count(loc)
require.NoError(t, err)
assert.Equal(t, 1, count)
err = b.Delete(loc, "123456", store.SoftDelete)
assert.NotNil(t, err)
+1 -1
View File
@@ -21,7 +21,7 @@ This library provides "social login" with Github, Google, Facebook and Yandex as
## Install
`go install github.com/go-pkgz/auth`
`go get -u github.com/go-pkgz/auth`
## Usage
+1 -1
View File
@@ -202,7 +202,7 @@ func (s *Service) AddProvider(name string, cid string, csecret string) {
case "facebook":
s.providers = append(s.providers, provider.NewService(provider.NewFacebook(p)))
case "yandex":
s.providers = append(s.providers, provider.NewService(provider.NewFacebook(p)))
s.providers = append(s.providers, provider.NewService(provider.NewYandex(p)))
case "dev":
s.providers = append(s.providers, provider.NewService(provider.NewDev(p)))
default:
+17
View File
@@ -73,6 +73,23 @@ func (u *User) IsAdmin() bool {
return u.BoolAttr(adminAttr)
}
// SliceAttr gets slice attribute
func (u *User) SliceAttr(key string) []string {
r, ok := u.Attributes[key].([]string)
if !ok {
return []string{}
}
return r
}
// SetSliceAttr sets boolean attribute
func (u *User) SetSliceAttr(key string, val []string) {
if u.Attributes == nil {
u.Attributes = map[string]interface{}{}
}
u.Attributes[key] = val
}
// HashID tries to hash val with hash.Hash and fallback to crc if needed
func HashID(h hash.Hash, val string) string {
+4 -3
View File
@@ -18,7 +18,7 @@ output looks like this:
2018/01/07 13:02:34.015 DEBUG {svc/handler.go:155 h.MyFunc2} some less important err message, file is too small`
```
_Without `lgr.CallerFile` it will drop `{caller}` part_
_Without `lgr.Caller*` it will drop `{caller}` part_
## details
@@ -33,8 +33,9 @@ _Without `lgr.CallerFile` it will drop `{caller}` part_
`lgr.New` call accepts functional options:
- `lgr.Debug` - turn debug mode on. This allows messages with "DEBUG" level (filtered overwise)
- `lgr.CallerFile` - adds the caller file info each message
- `lgr.CallerFunc` - adds the caller function info each message
- `lgr.CallerFile` - adds the caller file info
- `lgr.CallerFunc` - adds the caller function info
- `lgr.CallerPkg` - adds the caller package
- `lgr.LevelBraces` - wraps levels with "[" and "]"
- `lgr.Msec` - adds milliseconds to timestamp
- `lgr.Out(io.Writer)` - sets the output writer, default `os.Stdout`
+46 -12
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"io"
"os"
"path"
"runtime"
"strings"
"sync"
@@ -14,15 +15,18 @@ var levels = []string{"DEBUG", "INFO", "WARN", "ERROR", "PANIC", "FATAL"}
// Logger provided simple logger with basic support of levels. Thread safe
type Logger struct {
stdout, stderr io.Writer
dbg bool
lock sync.Mutex
callerFile, callerFunc bool
now nowFn
fatal panicFn
skipCallers int
levelBraces bool
msec bool
stdout, stderr io.Writer
dbg bool
lock sync.Mutex
callerFile bool
callerFunc bool
callerPkg bool
ignoredPkgCallers []string
now nowFn
fatal panicFn
skipCallers int
levelBraces bool
msec bool
}
type nowFn func() time.Time
@@ -67,15 +71,16 @@ func (l *Logger) Logf(format string, args ...interface{}) {
bld.WriteString(l.formatLevel(lv))
bld.WriteString(" ")
if l.dbg && (l.callerFile || l.callerFunc) {
if l.callerFile || l.callerFunc || l.callerPkg {
if pc, file, line, ok := runtime.Caller(l.skipCallers); ok {
funcName := ""
funcName, fileInfo := "", ""
if l.callerFunc {
funcNameElems := strings.Split(runtime.FuncForPC(pc).Name(), "/")
funcName = funcNameElems[len(funcNameElems)-1]
}
fileInfo := ""
if l.callerFile {
fnameElems := strings.Split(file, "/")
fileInfo = fmt.Sprintf("%s:%d", strings.Join(fnameElems[len(fnameElems)-2:], "/"), line)
@@ -83,6 +88,14 @@ func (l *Logger) Logf(format string, args ...interface{}) {
fileInfo += " "
}
}
// callerPkg only if no other callers
if l.callerPkg && !l.callerFile && !l.callerFunc {
file = l.ignoreCaller(file)
_, fileInfo = path.Split(path.Dir(file))
if l.callerFunc {
fileInfo += " "
}
}
srcFileInfo := fmt.Sprintf("{%s%s} ", fileInfo, funcName)
bld.WriteString(srcFileInfo)
}
@@ -108,6 +121,15 @@ func (l *Logger) Logf(format string, args ...interface{}) {
l.lock.Unlock()
}
func (l *Logger) ignoreCaller(p string) string {
for _, s := range l.ignoredPkgCallers {
if strings.Contains(p, "/"+s+"/") {
return strings.Replace(p, "/"+s, "", 1)
}
}
return p
}
func (l *Logger) formatLevel(lv string) string {
brace := func(b string) string {
@@ -183,6 +205,18 @@ func CallerFunc(l *Logger) {
l.callerFunc = true
}
// CallerPkg adds caller's package name
func CallerPkg(l *Logger) {
l.callerPkg = true
}
// CallerIgnore sets packages skipped from logging caller
func CallerIgnore(ignores ...string) Option {
return func(l *Logger) {
l.ignoredPkgCallers = ignores
}
}
// LevelBraces adds [] to level
func LevelBraces(l *Logger) {
l.levelBraces = true
+28 -5
View File
@@ -24,6 +24,15 @@
.document_theme_dark a {
color: #5e5eff;
}
.widget {
margin-top: 1rem;
}
.widget__comments-frame {
border: 1px dashed #aaa;
min-width: 35rem;
}
</style>
<script>
@@ -46,11 +55,18 @@
<div class="container">
<h1>Demo page</h1>
<p>To install widgets on your website, follow the <a href="https://github.com/umputun/remark#setup-on-your-website">instructions</a>.</p>
<p>See also</p>
<ul>
<li><a href="/web/last-comments.html">Last comments widget</a></li>
<li><a href="/web/counter.html">Counter widget</a></li>
</ul>
<div class="widgets">
<div class="widget widgets__widget ">
<a class="widget__link" href="/web/last-comments.html">Last comments widget page</a><br/>
<iframe class="widget__frame widget__comments-frame" src="/web/last-comments.html" frameborder="0"></iframe>
</div>
<div class="widget widgets__widget">
<a class="widget__link" href="/web/counter.html">Counter widget page</a><br/>
<div class="widget__frame widget__counter-frame">
Comments count: <span class="remark42__counter"></span>
</div>
</div>
</div>
<p><Button onclick="toggleTheme()">Toggle theme</Button></p>
<div id="remark42"></div>
</div>
@@ -67,6 +83,13 @@
s.type = 'text/javascript';
(d.head || d.body).appendChild(s);
})();
(function() {
var d = document, s = d.createElement('script');
s.src = '/web/counter.js';
s.type = 'text/javascript';
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>
Please enable JavaScript to view the comments powered by Remark.