-
-
Notifications
You must be signed in to change notification settings - Fork 2k
🔥 feat: Add support for contextual logs #4241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
ReneWerner87
wants to merge
12
commits into
main
Choose a base branch
from
codex/reusable-log-template
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 6 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
bba8c4c
feat: reuse logger template rendering for contextual logs
ReneWerner87 03fc04e
refactor: update logger context handling to accept any type
ReneWerner87 8323c21
refactor: enhance context template error handling and improve logger …
ReneWerner87 0e3b43c
refactor: update logger context handling to use retainedContext for i…
ReneWerner87 43f712f
test: document serial context logger tests
ReneWerner87 c3aa623
test: cover nil context logger caller
ReneWerner87 1b5e637
test: tighten context logger coverage
ReneWerner87 2c3cffa
docs: keep logger middleware docs focused
ReneWerner87 64fee00
feat: add configurable log context formats
ReneWerner87 94ac462
feat: auto register logger middleware tags
ReneWerner87 696dcbc
Merge branch 'main' into codex/reusable-log-template
ReneWerner87 d509f58
fix: address logger template review feedback
ReneWerner87 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package logtemplate | ||
|
|
||
| import ( | ||
| "errors" | ||
| ) | ||
|
|
||
| // ErrParameterMissing indicates that a template parameter was referenced but not provided. | ||
| var ErrParameterMissing = errors.New("logtemplate: template parameter missing") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| package logtemplate | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "fmt" | ||
| "io" | ||
|
|
||
| "github.com/gofiber/utils/v2" | ||
| ) | ||
|
|
||
| const ( | ||
| startTag = "${" | ||
| endTag = "}" | ||
| paramSeparator = ":" | ||
| ) | ||
|
|
||
| // Buffer abstracts the buffer operations used when rendering log templates. | ||
| type Buffer interface { | ||
| Len() int | ||
| ReadFrom(r io.Reader) (int64, error) | ||
| WriteTo(w io.Writer) (int64, error) | ||
| Bytes() []byte | ||
| Write(p []byte) (int, error) | ||
| WriteByte(c byte) error | ||
| WriteString(s string) (int, error) | ||
| Set(p []byte) | ||
| SetString(s string) | ||
| String() string | ||
| } | ||
|
|
||
| // Func renders one dynamic template tag. | ||
| type Func[C, D any] func(output Buffer, ctx C, data *D, extraParam string) (int, error) | ||
|
|
||
| // Template is a precompiled log template. | ||
| type Template[C, D any] struct { | ||
| fixedParts [][]byte | ||
| funcChain []Func[C, D] | ||
| } | ||
|
|
||
| // Build parses format once and returns a reusable template. | ||
| func Build[C, D any](format string, tagFunctions map[string]Func[C, D]) (*Template[C, D], error) { | ||
| templateB := utils.UnsafeBytes(format) | ||
| startTagB := utils.UnsafeBytes(startTag) | ||
| endTagB := utils.UnsafeBytes(endTag) | ||
| paramSeparatorB := utils.UnsafeBytes(paramSeparator) | ||
|
|
||
| chainCapacity := 2*bytes.Count(templateB, startTagB) + 1 | ||
| fixedParts := make([][]byte, 0, chainCapacity) | ||
| funcChain := make([]Func[C, D], 0, chainCapacity) | ||
|
|
||
| for { | ||
| before, after, found := bytes.Cut(templateB, startTagB) | ||
| if !found { | ||
| break | ||
| } | ||
|
|
||
| funcChain = append(funcChain, nil) | ||
| fixedParts = append(fixedParts, before) | ||
|
|
||
| templateB = after | ||
| before, after, found = bytes.Cut(templateB, endTagB) | ||
| if !found { | ||
| funcChain = append(funcChain, nil) | ||
| fixedParts = append(fixedParts, startTagB) | ||
| break | ||
| } | ||
|
|
||
| tag, param, foundParam := bytes.Cut(before, paramSeparatorB) | ||
| if foundParam { | ||
| fn, ok := tagFunctions[utils.UnsafeString(tag)+paramSeparator] | ||
| if !ok { | ||
| return nil, fmt.Errorf("%w: %q", ErrParameterMissing, utils.UnsafeString(before)) | ||
| } | ||
| funcChain = append(funcChain, fn) | ||
| fixedParts = append(fixedParts, param) | ||
| } else if fn, ok := tagFunctions[utils.UnsafeString(before)]; ok { | ||
| funcChain = append(funcChain, fn) | ||
| fixedParts = append(fixedParts, nil) | ||
| } | ||
|
ReneWerner87 marked this conversation as resolved.
|
||
|
|
||
| templateB = after | ||
| } | ||
|
|
||
| funcChain = append(funcChain, nil) | ||
| fixedParts = append(fixedParts, templateB) | ||
|
|
||
| return &Template[C, D]{ | ||
| fixedParts: fixedParts, | ||
| funcChain: funcChain, | ||
| }, nil | ||
| } | ||
|
|
||
| // Chains returns the fixed template parts and functions used by Execute. | ||
| func (t *Template[C, D]) Chains() ([][]byte, []Func[C, D]) { | ||
| if t == nil { | ||
| return nil, nil | ||
| } | ||
| return t.fixedParts, t.funcChain | ||
| } | ||
|
|
||
| // Execute renders the template into output. | ||
| func (t *Template[C, D]) Execute(output Buffer, ctx C, data *D) error { | ||
| if t == nil { | ||
| return nil | ||
| } | ||
| return ExecuteChains(output, ctx, data, t.fixedParts, t.funcChain) | ||
| } | ||
|
|
||
| // ExecuteChains renders precompiled template chains into output. | ||
| func ExecuteChains[C, D any](output Buffer, ctx C, data *D, fixedParts [][]byte, funcChain []Func[C, D]) error { | ||
| for i, fn := range funcChain { | ||
| switch { | ||
| case fn == nil: | ||
| if _, err := output.Write(fixedParts[i]); err != nil { | ||
| return err | ||
| } | ||
| case fixedParts[i] == nil: | ||
| if _, err := fn(output, ctx, data, ""); err != nil { | ||
| return err | ||
| } | ||
| default: | ||
| if _, err := fn(output, ctx, data, utils.UnsafeString(fixedParts[i])); err != nil { | ||
| return err | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package logtemplate | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/require" | ||
| "github.com/valyala/bytebufferpool" | ||
| ) | ||
|
|
||
| type testData struct { | ||
| value string | ||
| } | ||
|
|
||
| func Test_Template_Execute(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tmpl, err := Build[string, testData]("a ${tag} ${param:name} z", map[string]Func[string, testData]{ | ||
| "tag": func(output Buffer, ctx string, data *testData, _ string) (int, error) { | ||
| return output.WriteString(ctx + data.value) | ||
| }, | ||
| "param:": func(output Buffer, _ string, _ *testData, extraParam string) (int, error) { | ||
| return output.WriteString(extraParam) | ||
| }, | ||
| }) | ||
| require.NoError(t, err) | ||
|
|
||
| buf := bytebufferpool.Get() | ||
| defer bytebufferpool.Put(buf) | ||
|
|
||
| err = tmpl.Execute(buf, "ctx-", &testData{value: "data"}) | ||
| require.NoError(t, err) | ||
| require.Equal(t, "a ctx-data name z", buf.String()) | ||
| } | ||
|
|
||
| func Test_Template_MissingParameterTag(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| _, err := Build[string, testData]("${missing:name}", nil) | ||
| require.ErrorIs(t, err, ErrParameterMissing) | ||
| require.ErrorContains(t, err, "missing:name") | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.