id, mimetype, kind and content
This commit is contained in:
13
chk/chk.go
Normal file
13
chk/chk.go
Normal file
@@ -0,0 +1,13 @@
|
||||
// Package chk is a convenience shortcut to use shorter names to access the lol.Logger.
|
||||
package chk
|
||||
|
||||
import (
|
||||
"github.com/mleku/transit/lol"
|
||||
)
|
||||
|
||||
var F, E, W, I, D, T lol.Chk
|
||||
|
||||
func init() {
|
||||
F, E, W, I, D, T = lol.Main.Check.F, lol.Main.Check.E, lol.Main.Check.W, lol.Main.Check.I,
|
||||
lol.Main.Check.D, lol.Main.Check.T
|
||||
}
|
||||
61
content/content.go
Normal file
61
content/content.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package content
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/mleku/transit/chk"
|
||||
"github.com/mleku/transit/errorf"
|
||||
)
|
||||
|
||||
var BinaryPrefix = []byte("base64:")
|
||||
|
||||
// C is a content field for an event.T that can be binary or text, if set to binary, it encodes
|
||||
// to base64 URL format (with padding). Binary coded values have the prefix "base64:" prefix.
|
||||
// Setting binary should be done in accordance with the mimetype being
|
||||
// "application/octet-stream"
|
||||
type C struct {
|
||||
Val []byte
|
||||
Binary bool
|
||||
}
|
||||
|
||||
// New creates a new mime.Type based on input string or bytes of value.
|
||||
func New[V []byte | string](c V, binary bool) (content *C) {
|
||||
content = &C{Val: []byte(c), Binary: binary}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *C) MarshalJSON() (b []byte, err error) {
|
||||
if c.Binary {
|
||||
b = make([]byte, base64.URLEncoding.EncodedLen(len(c.Val))+2+len(BinaryPrefix))
|
||||
copy(b[1:], BinaryPrefix)
|
||||
base64.URLEncoding.Encode(b[1+len(BinaryPrefix):len(b)-1], c.Val)
|
||||
} else {
|
||||
b = make([]byte, len(c.Val)+2)
|
||||
copy(b[1:], c.Val)
|
||||
}
|
||||
b[0] = '"'
|
||||
b[len(b)-1] = '"'
|
||||
return
|
||||
}
|
||||
|
||||
func (c *C) UnmarshalJSON(b []byte) (err error) {
|
||||
b = b[1:]
|
||||
if bytes.HasPrefix(b, BinaryPrefix) {
|
||||
c.Binary = true
|
||||
// remove quotes and binary prefix
|
||||
b = b[len(BinaryPrefix) : len(b)-1]
|
||||
var n int
|
||||
// we use AppendDecode to avoid needing to trim pad bytes
|
||||
if c.Val, err = base64.URLEncoding.AppendDecode(c.Val, b); chk.E(err) {
|
||||
err = errorf.E("error: decoding failed at %d: %s", n, err.Error())
|
||||
return
|
||||
}
|
||||
c.Binary = true
|
||||
} else {
|
||||
b = b[:len(b)-1]
|
||||
c.Val = b
|
||||
c.Binary = false
|
||||
}
|
||||
return
|
||||
}
|
||||
50
content/content_test.go
Normal file
50
content/content_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package content
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"lukechampine.com/frand"
|
||||
|
||||
"github.com/mleku/transit/chk"
|
||||
"github.com/mleku/transit/log"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
content := "this is some plain text"
|
||||
log.I.F("%s", content)
|
||||
c := New(content, false)
|
||||
var err error
|
||||
var b []byte
|
||||
if b, err = json.Marshal(&c); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
log.I.F("%s", b)
|
||||
c2 := &C{}
|
||||
if err = json.Unmarshal(b, c2); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
log.I.F("%s", c2.Val)
|
||||
if !bytes.Equal(c.Val, c2.Val) {
|
||||
t.Fatal("failed to encode/decode")
|
||||
}
|
||||
bin := make([]byte, frand.Intn(100)+16)
|
||||
if _, err = frand.Read(bin); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bin1 := New(bin, true)
|
||||
if b, err = json.Marshal(bin1); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
log.I.S(bin1)
|
||||
log.I.S(b)
|
||||
bin2 := &C{}
|
||||
if err = json.Unmarshal(b, bin2); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
log.I.S(bin2)
|
||||
if !bytes.Equal(bin1.Val, bin2.Val) {
|
||||
t.Fatal("failed to encode/decode")
|
||||
}
|
||||
}
|
||||
12
errorf/errorf.go
Normal file
12
errorf/errorf.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Package errorf is a convenience shortcut to use shorter names to access the lol.Logger.
|
||||
package errorf
|
||||
|
||||
import (
|
||||
"github.com/mleku/transit/lol"
|
||||
)
|
||||
|
||||
var F, E, W, I, D, T lol.Err
|
||||
|
||||
func init() {
|
||||
F, E, W, I, D, T = lol.Main.Errorf.F, lol.Main.Errorf.E, lol.Main.Errorf.W, lol.Main.Errorf.I, lol.Main.Errorf.D, lol.Main.Errorf.T
|
||||
}
|
||||
26
event/event.go
Normal file
26
event/event.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"github.com/mleku/transit/content"
|
||||
"github.com/mleku/transit/id"
|
||||
"github.com/mleku/transit/kind"
|
||||
"github.com/mleku/transit/mime"
|
||||
)
|
||||
|
||||
// E is an event on the transit protocol.
|
||||
type E struct {
|
||||
// Id is the hash of the canonical format of the event.E.
|
||||
Id *id.I `json:"id" doc:"hash of canonical form of event in unpadded base64 URL encoding"`
|
||||
// Mimetype is the type of data contained in the Content field of an event.E.
|
||||
Mimetype *mime.Type `json:"mimetype" doc:"standard mimetype descriptor for the format of the content field"`
|
||||
// Kind is a short name of the application/intent of the event.
|
||||
Kind *kind.K `json:"kind" doc:"short name of the application/intent of the event"`
|
||||
// Content is the payload of the event.E.
|
||||
Content *content.C
|
||||
// Tags are a set of descriptors formatted as a key, value and optional further descriptors.
|
||||
|
||||
}
|
||||
|
||||
// C is the canonical format for an event.E that is hashed to generate the id.I.
|
||||
type C struct {
|
||||
}
|
||||
17
go.mod
Normal file
17
go.mod
Normal file
@@ -0,0 +1,17 @@
|
||||
module github.com/mleku/transit
|
||||
|
||||
go 1.24.3
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/fatih/color v1.18.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
lukechampine.com/frand v1.5.1 // indirect
|
||||
)
|
||||
23
go.sum
Normal file
23
go.sum
Normal file
@@ -0,0 +1,23 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
|
||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
|
||||
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
|
||||
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
|
||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
lukechampine.com/frand v1.5.1 h1:fg0eRtdmGFIxhP5zQJzM1lFDbD6CUfu/f+7WgAZd5/w=
|
||||
lukechampine.com/frand v1.5.1/go.mod h1:4VstaWc2plN4Mjr10chUD46RAVGWhpkZ5Nja8+Azp0Q=
|
||||
52
id/id.go
Normal file
52
id/id.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package id
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/zeebo/blake3"
|
||||
|
||||
"github.com/mleku/transit/chk"
|
||||
"github.com/mleku/transit/errorf"
|
||||
)
|
||||
|
||||
const Len = 32
|
||||
|
||||
var EncodedLen = base64.RawURLEncoding.EncodedLen(Len)
|
||||
|
||||
// I is a blake3 hash encoded in base64 URL format.
|
||||
type I struct {
|
||||
Hash []byte
|
||||
}
|
||||
|
||||
// New creates a new id.I based on a provided message bytes.
|
||||
func New[V []byte | string](msg V) (i *I) {
|
||||
sum := blake3.Sum256([]byte(msg))
|
||||
i = &I{Hash: sum[:]}
|
||||
return
|
||||
}
|
||||
|
||||
func (i *I) MarshalJSON() (b []byte, err error) {
|
||||
if len(i.Hash) != Len {
|
||||
err = errorf.E("id must be %d bytes long, got %d", Len, len(i.Hash))
|
||||
return
|
||||
}
|
||||
b = make([]byte, EncodedLen+2)
|
||||
b[0] = '"'
|
||||
b[len(b)-1] = '"'
|
||||
base64.RawURLEncoding.Encode(b[1:], i.Hash)
|
||||
return
|
||||
}
|
||||
|
||||
func (i *I) UnmarshalJSON(b []byte) (err error) {
|
||||
if len(b) != EncodedLen+2 {
|
||||
err = errorf.E("encoded id hash must be %d bytes long, got %d", EncodedLen+2, len(b))
|
||||
return
|
||||
}
|
||||
i.Hash = make([]byte, Len)
|
||||
var n int
|
||||
if n, err = base64.RawURLEncoding.Decode(i.Hash, b[1:EncodedLen+1]); chk.E(err) {
|
||||
err = errorf.E("error: decoding failed at %d: %s", n, err.Error())
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
29
id/id_test.go
Normal file
29
id/id_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package id
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/mleku/transit/chk"
|
||||
"github.com/mleku/transit/log"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
msg := "this is a test"
|
||||
i := New(msg)
|
||||
var err error
|
||||
var b []byte
|
||||
if b, err = json.Marshal(&i); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
log.I.F("encode %0x %s", i.Hash, b)
|
||||
i2 := &I{}
|
||||
if err = json.Unmarshal(b, i2); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
log.I.F("decode %0x", i2.Hash)
|
||||
if !bytes.Equal(i.Hash, i2.Hash) {
|
||||
t.Fatal("failed to encode/decode")
|
||||
}
|
||||
}
|
||||
25
kind/kind.go
Normal file
25
kind/kind.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package kind
|
||||
|
||||
// K is a name for the application/intent of the event (eg post, reply, auth, etc)
|
||||
type K struct {
|
||||
Val []byte
|
||||
}
|
||||
|
||||
// New creates a new kind.K based on input string or bytes of value.
|
||||
func New[V []byte | string](ks V) (k *K) {
|
||||
k = &K{Val: []byte(ks)}
|
||||
return
|
||||
}
|
||||
|
||||
func (k *K) MarshalJSON() (b []byte, err error) {
|
||||
b = make([]byte, len(k.Val)+2)
|
||||
copy(b[1:], k.Val)
|
||||
b[0] = '"'
|
||||
b[len(k.Val)+1] = '"'
|
||||
return
|
||||
}
|
||||
|
||||
func (k *K) UnmarshalJSON(b []byte) (err error) {
|
||||
k.Val = b[1 : len(b)-1]
|
||||
return
|
||||
}
|
||||
30
kind/kind_test.go
Normal file
30
kind/kind_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package kind
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/mleku/transit/chk"
|
||||
"github.com/mleku/transit/log"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
kind := "reply"
|
||||
log.I.F("%s", kind)
|
||||
k := New(kind)
|
||||
var err error
|
||||
var b []byte
|
||||
if b, err = json.Marshal(&k); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
log.I.F("%s", b)
|
||||
k2 := &K{}
|
||||
if err = json.Unmarshal(b, k2); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
log.I.F("%s", k2.Val)
|
||||
if !bytes.Equal(k.Val, k2.Val) {
|
||||
t.Fatal("failed to encode/decode")
|
||||
}
|
||||
}
|
||||
12
log/log.go
Normal file
12
log/log.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Package log is a convenience shortcut to use shorter names to access the lol.Logger.
|
||||
package log
|
||||
|
||||
import (
|
||||
"github.com/mleku/transit/lol"
|
||||
)
|
||||
|
||||
var F, E, W, I, D, T lol.LevelPrinter
|
||||
|
||||
func init() {
|
||||
F, E, W, I, D, T = lol.Main.Log.F, lol.Main.Log.E, lol.Main.Log.W, lol.Main.Log.I, lol.Main.Log.D, lol.Main.Log.T
|
||||
}
|
||||
18
lol/README.md
Normal file
18
lol/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# lol
|
||||
|
||||
location of log
|
||||
|
||||
This is a very simple, but practical library for logging in applications. Its
|
||||
main feature is printing source code locations to make debugging easier.
|
||||
|
||||
## terminals
|
||||
|
||||
Due to how so few terminals actually support source location hyperlinks, pretty much tilix and intellij terminal are
|
||||
the only two that really provide adequate functionality, this logging library defaults to output format that works
|
||||
best with intellij. As such, the terminal is aware of the CWD and the code locations printed are relative, as
|
||||
required to get the hyperlinkization from this terminal. Handling support for Tilix requires more complications and
|
||||
due to advances with IntelliJ's handling it is not practical to support any other for this purpose. Users of this
|
||||
library can always fall back to manually interpreting and accessing the relative file path to find the source of a log.
|
||||
|
||||
In addition, due to this terminal's slow rendering of long lines, long log strings are automatically broken into 80
|
||||
character lines, and if there is comma separators in the line, the line is broken at the comma instead of at column80. This works perfectly for this purpose.
|
||||
368
lol/log.go
Normal file
368
lol/log.go
Normal file
@@ -0,0 +1,368 @@
|
||||
// Package lol (log of location) is a simple logging library that prints a high precision unix
|
||||
// timestamp and the source location of a log print to make tracing errors simpler. Includes a
|
||||
// set of logging levels and the ability to filter out higher log levels for a more quiet
|
||||
// output.
|
||||
package lol
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
const (
|
||||
Off = iota
|
||||
Fatal
|
||||
Error
|
||||
Warn
|
||||
Info
|
||||
Debug
|
||||
Trace
|
||||
)
|
||||
|
||||
var LevelNames = []string{
|
||||
"off",
|
||||
"fatal",
|
||||
"error",
|
||||
"warn",
|
||||
"info",
|
||||
"debug",
|
||||
"trace",
|
||||
}
|
||||
|
||||
type (
|
||||
// LevelPrinter defines a set of terminal printing primitives that output with extra data,
|
||||
// time, log logLevelList, and code location
|
||||
|
||||
// Ln prints lists of interfaces with spaces in between
|
||||
Ln func(a ...interface{})
|
||||
// F prints like fmt.Println surrounded []byte log details
|
||||
F func(format string, a ...interface{})
|
||||
// S prints a spew.Sdump for an enveloper slice
|
||||
S func(a ...interface{})
|
||||
// C accepts a function so that the extra computation can be avoided if it is not being
|
||||
// viewed
|
||||
C func(closure func() string)
|
||||
// Chk is a shortcut for printing if there is an error, or returning true
|
||||
Chk func(e error) bool
|
||||
// Err is a pass-through function that uses fmt.Errorf to construct an error and returns the
|
||||
// error after printing it to the log
|
||||
Err func(format string, a ...any) error
|
||||
|
||||
// LevelPrinter is the set of log printers on each log level.
|
||||
LevelPrinter struct {
|
||||
Ln
|
||||
F
|
||||
S
|
||||
C
|
||||
Chk
|
||||
Err
|
||||
}
|
||||
|
||||
// LevelSpec is the name, ID and Colorizer for a log level.
|
||||
LevelSpec struct {
|
||||
ID int
|
||||
Name string
|
||||
Colorizer func(a ...any) string
|
||||
}
|
||||
|
||||
// Entry is a log entry to be printed as json to the log file
|
||||
Entry struct {
|
||||
Time time.Time
|
||||
Level string
|
||||
Package string
|
||||
CodeLocation string
|
||||
Text string
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// Writer can be swapped out for any io.*Writer* that you want to use instead of stdout.
|
||||
Writer io.Writer = os.Stderr
|
||||
|
||||
// LevelSpecs specifies the id, string name and color-printing function
|
||||
LevelSpecs = []LevelSpec{
|
||||
{Off, "", NoSprint},
|
||||
{Fatal, "FTL", color.New(color.BgRed, color.FgHiWhite).Sprint},
|
||||
{Error, "ERR", color.New(color.FgHiRed).Sprint},
|
||||
{Warn, "WRN", color.New(color.FgHiYellow).Sprint},
|
||||
{Info, "INF", color.New(color.FgHiGreen).Sprint},
|
||||
{Debug, "DBG", color.New(color.FgHiBlue).Sprint},
|
||||
{Trace, "TRC", color.New(color.FgHiMagenta).Sprint},
|
||||
}
|
||||
NoTimeStamp atomic.Bool
|
||||
ShortLoc atomic.Bool
|
||||
)
|
||||
|
||||
// NoSprint is a noop for sprint (it returns nothing no matter what is given to it).
|
||||
func NoSprint(a ...any) string { return "" }
|
||||
|
||||
// Log is a set of log printers for the various Level items.
|
||||
type Log struct {
|
||||
F, E, W, I, D, T LevelPrinter
|
||||
}
|
||||
|
||||
// Check is the set of log levels for a Check operation (prints an error if the error is not
|
||||
// nil).
|
||||
type Check struct {
|
||||
F, E, W, I, D, T Chk
|
||||
}
|
||||
|
||||
// Errorf prints an error that is also returned as an error, so the error is logged at the site.
|
||||
type Errorf struct {
|
||||
F, E, W, I, D, T Err
|
||||
}
|
||||
|
||||
// Logger is a collection of things that creates a logger, including levels.
|
||||
type Logger struct {
|
||||
*Log
|
||||
*Check
|
||||
*Errorf
|
||||
}
|
||||
|
||||
// Level is the level that the logger is printing at.
|
||||
var Level atomic.Int32
|
||||
|
||||
// Main is the main logger.
|
||||
var Main = &Logger{}
|
||||
|
||||
func init() {
|
||||
// Main = &Logger{}
|
||||
Main.Log, Main.Check, Main.Errorf = New(os.Stderr, 2)
|
||||
SetLoggers(Info)
|
||||
}
|
||||
|
||||
// SetLoggers configures a log level.
|
||||
func SetLoggers(level int) {
|
||||
Main.Log.T.F("log level %s", LevelSpecs[level].Colorizer(LevelNames[level]))
|
||||
Level.Store(int32(level))
|
||||
}
|
||||
|
||||
// GetLogLevel returns the log level number of a string log level.
|
||||
func GetLogLevel(level string) (i int) {
|
||||
for i = range LevelNames {
|
||||
if level == LevelNames[i] {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return Info
|
||||
}
|
||||
|
||||
// SetLogLevel sets the log level of the logger.
|
||||
func SetLogLevel(level string) {
|
||||
for i := range LevelNames {
|
||||
if level == LevelNames[i] {
|
||||
SetLoggers(i)
|
||||
return
|
||||
}
|
||||
}
|
||||
SetLoggers(Trace)
|
||||
}
|
||||
|
||||
// JoinStrings joins together anything into a set of strings with space separating the items.
|
||||
func JoinStrings(a ...any) (s string) {
|
||||
for i := range a {
|
||||
s += fmt.Sprint(a[i])
|
||||
if i < len(a)-1 {
|
||||
s += " "
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var msgCol = color.New(color.FgBlue).Sprint
|
||||
|
||||
// GetPrinter returns a full logger that writes to the provided io.Writer.
|
||||
func GetPrinter(l int32, writer io.Writer, skip int) LevelPrinter {
|
||||
return LevelPrinter{
|
||||
Ln: func(a ...interface{}) {
|
||||
if Level.Load() < l {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(writer,
|
||||
"%s%s %s %s\n",
|
||||
msgCol(TimeStamper()),
|
||||
LevelSpecs[l].Colorizer(LevelSpecs[l].Name),
|
||||
JoinStrings(a...),
|
||||
msgCol(GetLoc(skip)),
|
||||
)
|
||||
},
|
||||
F: func(format string, a ...interface{}) {
|
||||
if Level.Load() < l {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(writer,
|
||||
"%s%s %s %s\n",
|
||||
msgCol(TimeStamper()),
|
||||
LevelSpecs[l].Colorizer(LevelSpecs[l].Name),
|
||||
fmt.Sprintf(format, a...),
|
||||
msgCol(GetLoc(skip)),
|
||||
)
|
||||
},
|
||||
S: func(a ...interface{}) {
|
||||
if Level.Load() < l {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(writer,
|
||||
"%s%s %s %s\n",
|
||||
msgCol(TimeStamper()),
|
||||
LevelSpecs[l].Colorizer(LevelSpecs[l].Name),
|
||||
spew.Sdump(a...),
|
||||
msgCol(GetLoc(skip)),
|
||||
)
|
||||
},
|
||||
C: func(closure func() string) {
|
||||
if Level.Load() < l {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(writer,
|
||||
"%s%s %s %s\n",
|
||||
msgCol(TimeStamper()),
|
||||
LevelSpecs[l].Colorizer(LevelSpecs[l].Name),
|
||||
closure(),
|
||||
msgCol(GetLoc(skip)),
|
||||
)
|
||||
},
|
||||
Chk: func(e error) bool {
|
||||
if Level.Load() < l {
|
||||
return e != nil
|
||||
}
|
||||
if e != nil {
|
||||
fmt.Fprintf(writer,
|
||||
"%s%s %s %s\n",
|
||||
msgCol(TimeStamper()),
|
||||
LevelSpecs[l].Colorizer(LevelSpecs[l].Name),
|
||||
e.Error(),
|
||||
msgCol(GetLoc(skip)),
|
||||
)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
Err: func(format string, a ...interface{}) error {
|
||||
if Level.Load() < l {
|
||||
fmt.Fprintf(writer,
|
||||
"%s%s %s %s\n",
|
||||
msgCol(TimeStamper()),
|
||||
LevelSpecs[l].Colorizer(LevelSpecs[l].Name, " "),
|
||||
fmt.Sprintf(format, a...),
|
||||
msgCol(GetLoc(skip)),
|
||||
)
|
||||
}
|
||||
return fmt.Errorf(format, a...)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetNullPrinter is a logger that doesn't log.
|
||||
func GetNullPrinter() LevelPrinter {
|
||||
return LevelPrinter{
|
||||
Ln: func(a ...interface{}) {},
|
||||
F: func(format string, a ...interface{}) {},
|
||||
S: func(a ...interface{}) {},
|
||||
C: func(closure func() string) {},
|
||||
Chk: func(e error) bool { return e != nil },
|
||||
Err: func(format string, a ...interface{}) error { return fmt.Errorf(format, a...) },
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new logger with all the levels and things.
|
||||
func New(writer io.Writer, skip int) (l *Log, c *Check, errorf *Errorf) {
|
||||
if writer == nil {
|
||||
writer = Writer
|
||||
}
|
||||
l = &Log{
|
||||
T: GetPrinter(Trace, writer, skip),
|
||||
D: GetPrinter(Debug, writer, skip),
|
||||
I: GetPrinter(Info, writer, skip),
|
||||
W: GetPrinter(Warn, writer, skip),
|
||||
E: GetPrinter(Error, writer, skip),
|
||||
F: GetPrinter(Fatal, writer, skip),
|
||||
}
|
||||
c = &Check{
|
||||
F: l.F.Chk,
|
||||
E: l.E.Chk,
|
||||
W: l.W.Chk,
|
||||
I: l.I.Chk,
|
||||
D: l.D.Chk,
|
||||
T: l.T.Chk,
|
||||
}
|
||||
errorf = &Errorf{
|
||||
F: l.F.Err,
|
||||
E: l.E.Err,
|
||||
W: l.W.Err,
|
||||
I: l.I.Err,
|
||||
D: l.D.Err,
|
||||
T: l.T.Err,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TimeStamper generates the timestamp for logs.
|
||||
func TimeStamper() (s string) {
|
||||
if NoTimeStamp.Load() {
|
||||
return
|
||||
}
|
||||
return time.Now().Format("2006-01-02T15:04:05Z07:00.000 ")
|
||||
}
|
||||
|
||||
// var wd, _ = os.Getwd()
|
||||
|
||||
// GetNLoc returns multiple levels of depth of code location from the current.
|
||||
func GetNLoc(n int) (output string) {
|
||||
for ; n > 1; n-- {
|
||||
output += fmt.Sprintf("%s\n", GetLoc(n))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var prefix string
|
||||
|
||||
func init() {
|
||||
// this enables us to remove the base of the path for a more compact code location string,
|
||||
// this can be used with tilix custom hyperlinks feature
|
||||
//
|
||||
// create a script called `setcurrent` in your PATH ( eg ~/.local/bin/setcurrent )
|
||||
//
|
||||
// #!/usr/bin/bash
|
||||
// echo $(pwd) > ~/.current
|
||||
//
|
||||
// set the following environment variable in your ~/.bashrc
|
||||
//
|
||||
// export PROMPT_COMMAND='setcurrent'
|
||||
//
|
||||
// using the following regular expressions, replacing the path as necessary, and setting
|
||||
// perhaps a different program than ide (this is for goland, i use an alias to the binary)
|
||||
//
|
||||
// ^((([a-zA-Z@0-9-_.]+/)+([a-zA-Z@0-9-_.]+)):([0-9]+)) ide --line $5 $(cat /home/mleku/.current)/$2
|
||||
// [ ]((([a-zA-Z@0-9-_./]+)+([a-zA-Z@0-9-_.]+)):([0-9]+)) ide --line $5 $(cat /home/mleku/.current)/$2
|
||||
// ([/](([a-zA-Z@0-9-_.]+/)+([a-zA-Z@0-9-_.]+)):([0-9]+)) ide --line $5 /$2
|
||||
//
|
||||
// and so long as you use this with an app containing /lol/log.go as this one is, this finds
|
||||
// that path and trims it off from the log line locations and in tilix you can click on the
|
||||
// file locations that are relative to the CWD where you are running the relay from. if this
|
||||
// is a remote machine, just go to the location where your source code is to make it work.
|
||||
//
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
prefix = file[:len(file)-10]
|
||||
}
|
||||
|
||||
// GetLoc returns the code location of the caller.
|
||||
func GetLoc(skip int) (output string) {
|
||||
_, file, line, _ := runtime.Caller(skip)
|
||||
if strings.Contains(file, "pkg/mod/") || !ShortLoc.Load() {
|
||||
} else {
|
||||
var split []string
|
||||
split = strings.Split(file, prefix)
|
||||
file = split[1]
|
||||
}
|
||||
output = fmt.Sprintf("%s:%d", file, line)
|
||||
return
|
||||
}
|
||||
25
mime/mime.go
Normal file
25
mime/mime.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package mime
|
||||
|
||||
// Type is a structured mimetype using standard mimetype notation to denote the type of data contained in an event.E.
|
||||
type Type struct {
|
||||
Val []byte
|
||||
}
|
||||
|
||||
// New creates a new mime.Type based on input string or bytes of value.
|
||||
func New[V []byte | string](c V) (mt *Type) {
|
||||
mt = &Type{Val: []byte(c)}
|
||||
return
|
||||
}
|
||||
|
||||
func (mt *Type) MarshalJSON() (b []byte, err error) {
|
||||
b = make([]byte, len(mt.Val)+2)
|
||||
copy(b[1:], mt.Val)
|
||||
b[0] = '"'
|
||||
b[len(mt.Val)+1] = '"'
|
||||
return
|
||||
}
|
||||
|
||||
func (mt *Type) UnmarshalJSON(b []byte) (err error) {
|
||||
mt.Val = b[1 : len(b)-1]
|
||||
return
|
||||
}
|
||||
30
mime/mine_test.go
Normal file
30
mime/mine_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package mime
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/mleku/transit/chk"
|
||||
"github.com/mleku/transit/log"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
kind := "text/asciidoc"
|
||||
log.I.F("%s", kind)
|
||||
mt := New(kind)
|
||||
var err error
|
||||
var b []byte
|
||||
if b, err = json.Marshal(&mt); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
log.I.F("%s", b)
|
||||
mt2 := &Type{}
|
||||
if err = json.Unmarshal(b, mt2); chk.E(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
log.I.F("%s", mt2.Val)
|
||||
if !bytes.Equal(mt.Val, mt2.Val) {
|
||||
t.Fatal("failed to encode/decode")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user