Skip to content

Commit

Permalink
[feat] Add xopconsole - human & machine readable logger (#100)
Browse files Browse the repository at this point in the history
Add the xopconsole base logger. xopconsole support replay and tries to
be human-readable.

Also: add a test for metadata duration attributes, fix OTEL base logger
handling of metadata duration.
  • Loading branch information
muir authored Dec 28, 2023
1 parent 0a9ba53 commit 88284b1
Show file tree
Hide file tree
Showing 38 changed files with 4,058 additions and 71 deletions.
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ span.go linguist-generated
sub.go linguist-generated
internal/enumer_test.go linguist-generated
xopat/attributes.go linguist-generated
xopbase/abbr.go linguist-generated
xopbase/base.go linguist-generated
xopbase/skip.go linguist-generated
xopcon/console.go linguist-generated
xopconsole/attributes.go linguist-generated
xopconsole/replay.go linguist-generated
xopjson/jsonlogger.go linguist-generated
xopjson/replay.go linguist-generated
xopotel/buffered.go linguist-generated
Expand Down
8 changes: 8 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@

# Big picture

- TIMES
- drop precision to microsecond?
- add timezone offset?

- rip out current redaction functions. They aren't right but it's not time
to fix them

Expand All @@ -25,6 +29,7 @@

# Just do it (build ready)


- check for unchecked casts

- xopresource
Expand Down Expand Up @@ -232,6 +237,9 @@
# Ideas to ponder
- new "LeveledLog" type that combines Log & Line, pre-baking the level, defined as
interface so that documentation isn't crazy
- would it be okay to have a dependency on OTEL? It would help for TraceState and it
would also be nice to directly reference SpanKind()
Expand Down
4 changes: 2 additions & 2 deletions internal/util/version/version_split.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package version

import (
"fmt"
"regexp"

"github.com/Masterminds/semver/v3"
"github.com/pkg/errors"
)

var namespaceVersionRE = regexp.MustCompile(`^(.+)[- ]v?(\d+\.\d+\.\d+(?:-\S+)?)$`)
Expand All @@ -19,7 +19,7 @@ func SplitVersionWithError(namespace string) (string, *semver.Version, error) {
}
sver, err := semver.StrictNewVersion(version)
if err != nil {
return "", nil, fmt.Errorf("semver '%s' is not valid: %w", version, err)
return "", nil, errors.Wrapf(err, "semver '%s' is not valid", version)
}
return namespace, sver, nil
}
Expand Down
4 changes: 2 additions & 2 deletions sub.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 2 additions & 4 deletions sub.zzzgo
Original file line number Diff line number Diff line change
Expand Up @@ -270,14 +270,14 @@ func (settings *LogSettings) TagLinesWithSpanSequence(b bool) {
settings.tagLinesWithSpanSequence = b
}

// PrefillText is prepended to any eventual Line.Msg() or Line.Tempalte().
// PrefillText is prepended to any eventual Line.Msg() or Line.Template().
// PrefillText will be ignored for Line.Model() and Line.Link().
func (sub *Sub) PrefillText(m string) *Sub {
sub.settings.PrefillText(m)
return sub
}

// PrefillText is prepended to any eventual Line.Msg() or Line.Tempalte()
// PrefillText is prepended to any eventual Line.Msg() or Line.Template()
// PrefillText will be ignored for Line.Model() and Line.Link().
func (settings *LogSettings) PrefillText(m string) {
settings.prefillMsg = m
Expand Down Expand Up @@ -395,8 +395,6 @@ func (settings *LogSettings) PrefillFloat32(k string, v float32) {
// line.
// PrefillZZZ is not threadsafe with respect to other calls on the same *Sub.
// Should not be used after Step(), Fork(), or Log() is called.
// CONDITIONAL ONLY:String
// Redaction is not supported.
// END CONDITIONAL
func (sub *Sub) PrefillZZZ(k string, v zzz) *Sub {
sub.settings.PrefillZZZ(k, v)
Expand Down
50 changes: 44 additions & 6 deletions tools/xopzzz/xopzzz.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
var macroRE = regexp.MustCompile(`^(\s*)//\s?MACRO (\w+)(?:\s+(SKIP|ONLY):(\S+))?\s*$`)
var errorRE = regexp.MustCompile(`^(\s*)//MACRO/`)
var indentRE = regexp.MustCompile(`^(\s*)(?:\S|$)`)
var zzzRE = regexp.MustCompile(`(zzz|Zzz|ZZZ)`)
var zzzRE = regexp.MustCompile(`(zzz|Zzz|ZZZ|zZZ)`)
var packageRE = regexp.MustCompile(`^package (\w+)`)
var conditionalRE = regexp.MustCompile(`^\s*//\s?CONDITIONAL (?:(?:ONLY:(\S+))|(?:SKIP:(\S+)))\s*$`)
var endConditionalRE = regexp.MustCompile(`^\s*//\s?END CONDITIONAL\s*$`)
Expand Down Expand Up @@ -201,7 +201,7 @@ var macros = map[string]map[string]string{
"SpanKindConsumer": "true",
},
// Note: these map to base types, not exact types. Exact types
// can be found in xopbase.StringToDataType
// are next.
"DataTypeAbbreviations": {
"i": "Int64",
"i8": "Int64",
Expand All @@ -225,6 +225,37 @@ var macros = map[string]map[string]string{
"enum": "Enum",
"error": "String",
},
"DataTypeAbbreviationsExact": {
"i": "Int",
"i8": "Int8",
"i16": "Int16",
"i32": "Int32",
"i64": "Int64",
"u": "Uint",
"u8": "Uint8",
"u16": "Uint16",
"u32": "Uint32",
"u64": "Uint64",
"uintptr": "Uintptr",
"f32": "Float32",
"f64": "Float64",
"any": "Any",
"bool": "Bool",
"dur": "Duration",
"time": "Time",
"s": "String",
"stringer": "Stringer",
"enum": "Enum",
"error": "Error",
},
"LogLevel": {
"Trace": "",
"Debug": "",
"Info": "",
"Warn": "",
"Error": "",
"Alert": "",
},
}

var allLines []string
Expand Down Expand Up @@ -275,7 +306,7 @@ func main() {
var toTitle = cases.Title(language.Und)

func macroExpand(indent string, macro string, skip bool, skipList string) {
m, ok := macros[macro]
values, ok := macros[macro]
if !ok {
panic(fmt.Errorf("'%s' isn't a valid macro, at line %d", macro, index+1))
}
Expand Down Expand Up @@ -304,8 +335,8 @@ func macroExpand(indent string, macro string, skip bool, skipList string) {
}
}

keys := make([]string, 0, len(m))
for k := range m {
keys := make([]string, 0, len(values))
for k := range values {
keys = append(keys, k)
}
sort.Strings(keys)
Expand All @@ -314,14 +345,15 @@ func macroExpand(indent string, macro string, skip bool, skipList string) {
if len(skips) > 0 && skip == ok {
continue
}
typ := m[name]
typ := values[name]
if currentPackage != "" {
typ = strings.TrimPrefix(typ, currentPackage+".")
}
replMap := map[string]string{
"ZZZ": name,
"zzz": typ,
"Zzz": toTitle.String(typ),
"zZZ": strings.ToLower(name),
}
var skipping bool
for _, line := range lines {
Expand All @@ -330,6 +362,9 @@ func macroExpand(indent string, macro string, skip bool, skipList string) {
// ONLY
skipping = true
for _, n := range strings.Split(m[1], ",") {
if _, ok := values[n]; !ok {
panic(fmt.Errorf("value %s is not part of %s", n, macro))
}
if n == name {
skipping = false
break
Expand All @@ -339,6 +374,9 @@ func macroExpand(indent string, macro string, skip bool, skipList string) {
// SKIP
skipping = false
for _, n := range strings.Split(m[2], ",") {
if _, ok := values[n]; !ok {
panic(fmt.Errorf("value %s is not part of %s", n, macro))
}
if n == name {
skipping = true
break
Expand Down
20 changes: 15 additions & 5 deletions xopat/attributes.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 15 additions & 5 deletions xopat/attributes.zzzgo
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import (
"sync"
"sync/atomic"

"github.com/pkg/errors"
"github.com/xoplog/xop-go/internal/util/version"
"github.com/xoplog/xop-go/xopproto"
"github.com/xoplog/xop-go/xoputil"

"github.com/Masterminds/semver/v3"
"github.com/pkg/errors"
)

var attributeCount int32 = 1
Expand All @@ -29,6 +30,7 @@ type Attribute struct {
version string
properties Make
jsonKey JSONKey
consoleKey []byte
exampleValue interface{}
reflectType reflect.Type
typeName string
Expand Down Expand Up @@ -133,10 +135,14 @@ func (s Make) make(registry *Registry, exampleValue interface{}, subType Attribu

jsonKey, err := json.Marshal(s.Key)
if err != nil {
return Attribute{}, fmt.Errorf("cannot marshal attribute name '%s': %w", s.Key, err)
return Attribute{}, errors.Wrapf(err, "cannot marshal attribute name '%s'", s.Key)
}
jsonKey = append(jsonKey, ':')

ck := xoputil.JBuilder{}
ck.AddConsoleString(s.Key)
ck.AppendByte('=')

namespace, sver, err := version.SplitVersionWithError(namespace)
if err != nil {
return Attribute{}, err
Expand All @@ -154,9 +160,10 @@ func (s Make) make(registry *Registry, exampleValue interface{}, subType Attribu
b: jsonKey,
s: string(jsonKey),
},
defSize: int32(len(namespace) + len(s.Key) + len(s.Description) + len(sver.String())),
semver: sver,
number: atomic.AddInt32(&attributeCount, 1),
consoleKey: ck.B,
defSize: int32(len(namespace) + len(s.Key) + len(s.Description) + len(sver.String())),
semver: sver,
number: atomic.AddInt32(&attributeCount, 1),
}
ra.jsonDef = jsonAttributeDefinition(&ra)
ra.jsonDefString = string(ra.jsonDef)
Expand All @@ -182,6 +189,8 @@ func (r Attribute) JSONKey() JSONKey { return r.jsonKey }
// ReflectType can be nil if the example value was nil
func (r Attribute) ReflectType() reflect.Type { return r.reflectType }

// ConsoleKey includes an =
func (r Attribute) ConsoleKey() []byte { return r.consoleKey }
func (r Attribute) Key() string { return r.properties.Key }
func (r Attribute) Description() string { return r.properties.Description }
func (r Attribute) Namespace() string { return r.namespace }
Expand All @@ -207,6 +216,7 @@ var _ AttributeInterface = &Attribute{}

type AttributeInterface interface {
JSONKey() JSONKey
ConsoleKey() []byte // includes an '='
ReflectType() reflect.Type
Key() string
Description() string
Expand Down
Loading

0 comments on commit 88284b1

Please sign in to comment.