mirror of
https://github.com/gitpod-io/gitpod.git
synced 2025-12-08 17:36:30 +00:00
142 lines
3.4 KiB
Go
142 lines
3.4 KiB
Go
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.
|
|
// Licensed under the GNU Affero General Public License (AGPL).
|
|
// See License.AGPL.txt in the project root for license information.
|
|
|
|
package jsonselect
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/buger/jsonparser"
|
|
"github.com/caddyserver/caddy/v2"
|
|
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
|
"github.com/caddyserver/caddy/v2/modules/logging"
|
|
"go.uber.org/zap/buffer"
|
|
"go.uber.org/zap/zapcore"
|
|
)
|
|
|
|
const (
|
|
moduleName = "jsonselect"
|
|
moduleID = "caddy.logging.encoders." + moduleName
|
|
)
|
|
|
|
func init() {
|
|
caddy.RegisterModule(JSONSelectEncoder{})
|
|
}
|
|
|
|
type JSONSelectEncoder struct {
|
|
logging.LogEncoderConfig
|
|
zapcore.Encoder `json:"-"`
|
|
Selector string `json:"selector,omitempty"`
|
|
|
|
getters [][]string
|
|
setters [][]string
|
|
}
|
|
|
|
func (JSONSelectEncoder) CaddyModule() caddy.ModuleInfo {
|
|
return caddy.ModuleInfo{
|
|
ID: moduleID,
|
|
New: func() caddy.Module {
|
|
return &JSONSelectEncoder{
|
|
Encoder: new(logging.JSONEncoder),
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
func (e *JSONSelectEncoder) Provision(ctx caddy.Context) error {
|
|
if e.Selector == "" {
|
|
return fmt.Errorf("selector is mandatory")
|
|
}
|
|
|
|
e.setters = [][]string{}
|
|
e.getters = [][]string{}
|
|
r := caddy.NewReplacer()
|
|
r.Map(func(sel string) (interface{}, bool) {
|
|
var set, get string
|
|
|
|
parts := strings.Split(sel, ":")
|
|
if len(parts) == 1 {
|
|
set = parts[0]
|
|
get = set
|
|
} else if len(parts) == 2 {
|
|
set = parts[0]
|
|
get = parts[1]
|
|
} else {
|
|
// todo > error out - how?
|
|
return nil, false
|
|
}
|
|
|
|
e.setters = append(e.setters, strings.Split(set, ">"))
|
|
e.getters = append(e.getters, strings.Split(get, ">"))
|
|
return nil, false
|
|
})
|
|
r.ReplaceAll(e.Selector, "")
|
|
|
|
if len(e.setters) != len(e.getters) {
|
|
return fmt.Errorf("selector must have the same number of setters and getters")
|
|
}
|
|
|
|
e.Encoder = zapcore.NewJSONEncoder(e.ZapcoreEncoderConfig())
|
|
return nil
|
|
}
|
|
|
|
func (e JSONSelectEncoder) Clone() zapcore.Encoder {
|
|
return JSONSelectEncoder{
|
|
LogEncoderConfig: e.LogEncoderConfig,
|
|
Encoder: e.Encoder.Clone(),
|
|
Selector: e.Selector,
|
|
getters: e.getters,
|
|
setters: e.setters,
|
|
}
|
|
}
|
|
|
|
func (e JSONSelectEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
|
|
buf, err := e.Encoder.EncodeEntry(entry, fields)
|
|
if err != nil {
|
|
return buf, err
|
|
}
|
|
|
|
res := []byte{'{', '}'}
|
|
// Temporary workaround the bug https://github.com/buger/jsonparser/issues/232
|
|
// TODO(leo): switch back to EachKey (see git history) for perf reasons when fixed
|
|
for idx, paths := range e.getters {
|
|
val, typ, _, err := jsonparser.Get(buf.Bytes(), paths...)
|
|
if err == jsonparser.KeyPathNotFoundError {
|
|
// path not found, skip
|
|
continue
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch typ {
|
|
case jsonparser.NotExist:
|
|
// path not found, skip
|
|
case jsonparser.String:
|
|
res, _ = jsonparser.Set(res, append(append([]byte{'"'}, val...), '"'), e.setters[idx]...)
|
|
default:
|
|
res, _ = jsonparser.Set(res, val, e.setters[idx]...)
|
|
}
|
|
}
|
|
|
|
// Reset the buffer to output our own content
|
|
buf.Reset()
|
|
// Insert the new content
|
|
nl := []byte("\n")
|
|
if !bytes.HasSuffix(res, nl) {
|
|
res = append(res, nl...)
|
|
}
|
|
buf.Write(res)
|
|
|
|
return buf, err
|
|
}
|
|
|
|
// Interface guards
|
|
var (
|
|
_ zapcore.Encoder = (*JSONSelectEncoder)(nil)
|
|
_ caddy.Provisioner = (*JSONSelectEncoder)(nil)
|
|
_ caddyfile.Unmarshaler = (*JSONSelectEncoder)(nil)
|
|
)
|