summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--metrics/metrics.go44
-rw-r--r--tpl/partials/integration_test.go70
-rw-r--r--tpl/partials/partials.go41
3 files changed, 125 insertions, 30 deletions
diff --git a/metrics/metrics.go b/metrics/metrics.go
index f48ee33e0..12f825e19 100644
--- a/metrics/metrics.go
+++ b/metrics/metrics.go
@@ -25,11 +25,9 @@ import (
"sync"
"time"
- "github.com/gohugoio/hugo/helpers"
-
"github.com/gohugoio/hugo/common/types"
-
"github.com/gohugoio/hugo/compare"
+ "github.com/gohugoio/hugo/helpers"
)
// The Provider interface defines an interface for measuring metrics.
@@ -42,7 +40,7 @@ type Provider interface {
WriteMetrics(w io.Writer)
// TrackValue tracks the value for diff calculations etc.
- TrackValue(key string, value interface{})
+ TrackValue(key string, value interface{}, cached bool)
// Reset clears the metric store.
Reset()
@@ -54,8 +52,6 @@ type diff struct {
simSum int
}
-var counter = 0
-
func (d *diff) add(v interface{}) *diff {
if types.IsNil(d.baseline) {
d.baseline = v
@@ -77,6 +73,8 @@ type Store struct {
mu sync.Mutex
diffs map[string]*diff
diffmu sync.Mutex
+ cached map[string]int
+ cachedmu sync.Mutex
}
// NewProvider returns a new instance of a metric store.
@@ -85,6 +83,7 @@ func NewProvider(calculateHints bool) Provider {
calculateHints: calculateHints,
metrics: make(map[string][]time.Duration),
diffs: make(map[string]*diff),
+ cached: make(map[string]int),
}
}
@@ -93,24 +92,24 @@ func (s *Store) Reset() {
s.mu.Lock()
s.metrics = make(map[string][]time.Duration)
s.mu.Unlock()
+
s.diffmu.Lock()
s.diffs = make(map[string]*diff)
s.diffmu.Unlock()
+
+ s.cachedmu.Lock()
+ s.cached = make(map[string]int)
+ s.cachedmu.Unlock()
}
// TrackValue tracks the value for diff calculations etc.
-func (s *Store) TrackValue(key string, value interface{}) {
+func (s *Store) TrackValue(key string, value interface{}, cached bool) {
if !s.calculateHints {
return
}
s.diffmu.Lock()
- var (
- d *diff
- found bool
- )
-
- d, found = s.diffs[key]
+ d, found := s.diffs[key]
if !found {
d = &diff{}
@@ -118,8 +117,13 @@ func (s *Store) TrackValue(key string, value interface{}) {
}
d.add(value)
-
s.diffmu.Unlock()
+
+ if cached {
+ s.cachedmu.Lock()
+ s.cached[key] = s.cached[key] + 1
+ s.cachedmu.Unlock()
+ }
}
// MeasureSince adds a measurement for key to the metric store.
@@ -155,17 +159,18 @@ func (s *Store) WriteMetrics(w io.Writer) {
}
avg := time.Duration(int(sum) / len(v))
+ cacheCount := s.cached[k]
- results[i] = result{key: k, count: len(v), max: max, sum: sum, avg: avg, cacheFactor: cacheFactor}
+ results[i] = result{key: k, count: len(v), max: max, sum: sum, avg: avg, cacheCount: cacheCount, cacheFactor: cacheFactor}
i++
}
s.mu.Unlock()
if s.calculateHints {
- fmt.Fprintf(w, " %9s %13s %12s %12s %5s %s\n", "cache", "cumulative", "average", "maximum", "", "")
- fmt.Fprintf(w, " %9s %13s %12s %12s %5s %s\n", "potential", "duration", "duration", "duration", "count", "template")
- fmt.Fprintf(w, " %9s %13s %12s %12s %5s %s\n", "-----", "----------", "--------", "--------", "-----", "--------")
+ fmt.Fprintf(w, " %13s %12s %12s %9s %7s %6s %5s %s\n", "cumulative", "average", "maximum", "cache", "percent", "cached", "total", "")
+ fmt.Fprintf(w, " %13s %12s %12s %9s %7s %6s %5s %s\n", "duration", "duration", "duration", "potential", "cached", "count", "count", "template")
+ fmt.Fprintf(w, " %13s %12s %12s %9s %7s %6s %5s %s\n", "----------", "--------", "--------", "---------", "-------", "------", "-----", "--------")
} else {
fmt.Fprintf(w, " %13s %12s %12s %5s %s\n", "cumulative", "average", "maximum", "", "")
fmt.Fprintf(w, " %13s %12s %12s %5s %s\n", "duration", "duration", "duration", "count", "template")
@@ -176,7 +181,7 @@ func (s *Store) WriteMetrics(w io.Writer) {
sort.Sort(bySum(results))
for _, v := range results {
if s.calculateHints {
- fmt.Fprintf(w, " %9d %13s %12s %12s %5d %s\n", v.cacheFactor, v.sum, v.avg, v.max, v.count, v.key)
+ fmt.Fprintf(w, " %13s %12s %12s %9d %7.f %6d %5d %s\n", v.sum, v.avg, v.max, v.cacheFactor, float64(v.cacheCount)/float64(v.count)*100, v.cacheCount, v.count, v.key)
} else {
fmt.Fprintf(w, " %13s %12s %12s %5d %s\n", v.sum, v.avg, v.max, v.count, v.key)
}
@@ -187,6 +192,7 @@ func (s *Store) WriteMetrics(w io.Writer) {
type result struct {
key string
count int
+ cacheCount int
cacheFactor int
sum time.Duration
max time.Duration
diff --git a/tpl/partials/integration_test.go b/tpl/partials/integration_test.go
new file mode 100644
index 000000000..f462f35f5
--- /dev/null
+++ b/tpl/partials/integration_test.go
@@ -0,0 +1,70 @@
+// Copyright 2022 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package partials_test
+
+import (
+ "testing"
+
+ "github.com/gohugoio/hugo/hugolib"
+)
+
+func TestInclude(t *testing.T) {
+ t.Parallel()
+
+ files := `
+-- config.toml --
+baseURL = 'http://example.com/'
+-- layouts/index.html --
+partial: {{ partials.Include "foo.html" . }}
+-- layouts/partials/foo.html --
+foo
+ `
+
+ b := hugolib.NewIntegrationTestBuilder(
+ hugolib.IntegrationTestConfig{
+ T: t,
+ TxtarString: files,
+ },
+ ).Build()
+
+ b.AssertFileContent("public/index.html", `
+partial: foo
+`)
+}
+
+func TestIncludeCached(t *testing.T) {
+ t.Parallel()
+
+ files := `
+-- config.toml --
+baseURL = 'http://example.com/'
+-- layouts/index.html --
+partialCached: {{ partials.IncludeCached "foo.html" . }}
+partialCached: {{ partials.IncludeCached "foo.html" . }}
+-- layouts/partials/foo.html --
+foo
+ `
+
+ b := hugolib.NewIntegrationTestBuilder(
+ hugolib.IntegrationTestConfig{
+ T: t,
+ TxtarString: files,
+ },
+ ).Build()
+
+ b.AssertFileContent("public/index.html", `
+partialCached: foo
+partialCached: foo
+`)
+}
diff --git a/tpl/partials/partials.go b/tpl/partials/partials.go
index b0dc0a997..d80ccfa4f 100644
--- a/tpl/partials/partials.go
+++ b/tpl/partials/partials.go
@@ -93,23 +93,41 @@ func (c *contextWrapper) Set(in interface{}) string {
// Else, the rendered output will be returned:
// A string if the partial is a text/template, or template.HTML when html/template.
func (ns *Namespace) Include(name string, contextList ...interface{}) (interface{}, error) {
- name = strings.TrimPrefix(name, "partials/")
+ name, result, err := ns.include(name, contextList...)
+ if err != nil {
+ return result, err
+ }
+
+ if ns.deps.Metrics != nil {
+ ns.deps.Metrics.TrackValue(name, result, false)
+ }
+ return result, nil
+}
+
+// include is a helper function that lookups and executes the named partial.
+// Returns the final template name and the rendered output.
+func (ns *Namespace) include(name string, contextList ...interface{}) (string, interface{}, error) {
var context interface{}
if len(contextList) > 0 {
context = contextList[0]
}
- n := "partials/" + name
- templ, found := ns.deps.Tmpl().Lookup(n)
+ var n string
+ if strings.HasPrefix(name, "partials/") {
+ n = name
+ } else {
+ n = "partials/" + name
+ }
+ templ, found := ns.deps.Tmpl().Lookup(n)
if !found {
// For legacy reasons.
templ, found = ns.deps.Tmpl().Lookup(n + ".html")
}
if !found {
- return "", fmt.Errorf("partial %q not found", name)
+ return "", "", fmt.Errorf("partial %q not found", name)
}
var info tpl.ParseInfo
@@ -136,7 +154,7 @@ func (ns *Namespace) Include(name string, contextList ...interface{}) (interface
}
if err := ns.deps.Tmpl().Execute(templ, w, context); err != nil {
- return "", err
+ return "", "", err
}
var result interface{}
@@ -149,11 +167,7 @@ func (ns *Namespace) Include(name string, contextList ...interface{}) (interface
result = template.HTML(w.(fmt.Stringer).String())
}
- if ns.deps.Metrics != nil {
- ns.deps.Metrics.TrackValue(templ.Name(), result)
- }
-
- return result, nil
+ return templ.Name(), result, nil
}
// IncludeCached executes and caches partial templates. The cache is created with name+variants as the key.
@@ -215,11 +229,16 @@ func (ns *Namespace) getOrCreate(key partialCacheKey, context interface{}) (resu
return p, nil
}
- p, err = ns.Include(key.name, context)
+ var name string
+ name, p, err = ns.include(key.name, context)
if err != nil {
return nil, err
}
+ if ns.deps.Metrics != nil {
+ ns.deps.Metrics.TrackValue(name, p, true)
+ }
+
ns.cachedPartials.Lock()
defer ns.cachedPartials.Unlock()
// Double-check.