Skip to content

Commit

Permalink
feat: add an option to provide a custom logger (#8)
Browse files Browse the repository at this point in the history
* feaT: add an option to provide a custom logger

* update elfdump
  • Loading branch information
ayoubfaouzi authored Jun 27, 2024
1 parent 3eadc59 commit 33b33db
Show file tree
Hide file tree
Showing 21 changed files with 1,157 additions and 13 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
go vet .
if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.21.x'

- name: Go vet
- name: Go Static Check
run: |
go install honnef.co/go/tools/cmd/staticcheck@latest
staticcheck .
Expand Down
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,3 @@ func main() {

- https://refspecs.linuxfoundation.org/elf/elf.pdf
- https://github.com/freebsd/freebsd-src/blob/main/sys/sys/elf_common.h


2 changes: 1 addition & 1 deletion cmd/elfdump.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func dumpFileHeader(fileHdr elf.FileHeader) {
}

func parse(filename string, cfg config) {
p, err := elf.New(filename)
p, err := elf.New(filename, nil)
defer p.CloseFile()
if err != nil {
panic(err)
Expand Down
4 changes: 4 additions & 0 deletions file.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package elf
import (
"encoding/binary"
"errors"

"github.com/saferwall/elf/log"
)

// FileIdent is a representation of the raw ident array (first 16 bytes of an ELF file)
Expand Down Expand Up @@ -65,6 +67,8 @@ type File struct {
ELFBin32 `json:",omitempty"`
ELFBin64 `json:",omitempty"`
ELFSymbols `json:",omitempty"`
opts *Options
logger *log.Helper
}

func NewBinaryFile() *File {
Expand Down
42 changes: 42 additions & 0 deletions log/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Logger

This code was taken from the go microservice framework [kratos](https://github.com/go-kratos/kratos).

## Usage

### Structured logging

```go
logger := log.NewStdLogger(os.Stdout)
// fields & valuer
logger = log.With(logger,
"service.name", "hellworld",
"service.version", "v1.0.0",
"ts", log.DefaultTimestamp,
"caller", log.DefaultCaller,
)
logger.Log(log.LevelInfo, "key", "value")

// helper
helper := log.NewHelper(logger)
helper.Log(log.LevelInfo, "key", "value")
helper.Info("info message")
helper.Infof("info %s", "message")
helper.Infow("key", "value")

// filter
log := log.NewHelper(log.NewFilter(logger,
log.FilterLevel(log.LevelInfo),
log.FilterKey("foo"),
log.FilterValue("bar"),
log.FilterFunc(customFilter),
))
log.Debug("debug log")
log.Info("info log")
log.Warn("warn log")
log.Error("warn log")
```

## Third party log library

If you need to implement a third party logging library like `zap`, have a look this [url](https://github.com/go-kratos/kratos/tree/main/contrib/log).
96 changes: 96 additions & 0 deletions log/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package log

// FilterOption is filter option.
type FilterOption func(*Filter)

const fuzzyStr = "***"

// FilterLevel with filter level.
func FilterLevel(level Level) FilterOption {
return func(opts *Filter) {
opts.level = level
}
}

// FilterKey with filter key.
func FilterKey(key ...string) FilterOption {
return func(o *Filter) {
for _, v := range key {
o.key[v] = struct{}{}
}
}
}

// FilterValue with filter value.
func FilterValue(value ...string) FilterOption {
return func(o *Filter) {
for _, v := range value {
o.value[v] = struct{}{}
}
}
}

// FilterFunc with filter func.
func FilterFunc(f func(level Level, keyvals ...interface{}) bool) FilterOption {
return func(o *Filter) {
o.filter = f
}
}

// Filter is a logger filter.
type Filter struct {
logger Logger
level Level
key map[interface{}]struct{}
value map[interface{}]struct{}
filter func(level Level, keyvals ...interface{}) bool
}

// NewFilter new a logger filter.
func NewFilter(logger Logger, opts ...FilterOption) *Filter {
options := Filter{
logger: logger,
key: make(map[interface{}]struct{}),
value: make(map[interface{}]struct{}),
}
for _, o := range opts {
o(&options)
}
return &options
}

// Log Print log by level and keyvals.
func (f *Filter) Log(level Level, keyvals ...interface{}) error {
if level < f.level {
return nil
}
// fkv is used to provide a slice to contains both logger.prefix and keyvals for filter
var fkv []interface{}
if l, ok := f.logger.(*logger); ok {
if len(l.prefix) > 0 {
fkv = make([]interface{}, 0, len(l.prefix)+len(keyvals))
fkv = append(fkv, l.prefix...)
fkv = append(fkv, keyvals...)
}
} else {
fkv = keyvals
}
if f.filter != nil && f.filter(level, fkv...) {
return nil
}
if len(f.key) > 0 || len(f.value) > 0 {
for i := 0; i < len(keyvals); i += 2 {
v := i + 1
if v >= len(keyvals) {
continue
}
if _, ok := f.key[keyvals[i]]; ok {
keyvals[v] = fuzzyStr
}
if _, ok := f.value[keyvals[v]]; ok {
keyvals[v] = fuzzyStr
}
}
}
return f.logger.Log(level, keyvals...)
}
133 changes: 133 additions & 0 deletions log/filter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package log

import (
"bytes"
"io"
"testing"
)

func TestFilterAll(t *testing.T) {
logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
log := NewHelper(NewFilter(logger,
FilterLevel(LevelDebug),
FilterKey("username"),
FilterValue("hello"),
FilterFunc(testFilterFunc),
))
log.Log(LevelDebug, "msg", "test debug")
log.Info("hello")
log.Infow("password", "123456")
log.Infow("username", "kratos")
log.Warn("warn log")
}

func TestFilterLevel(t *testing.T) {
logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
log := NewHelper(NewFilter(NewFilter(logger, FilterLevel(LevelWarn))))
log.Log(LevelDebug, "msg1", "te1st debug")
log.Debug("test debug")
log.Debugf("test %s", "debug")
log.Debugw("log", "test debug")
log.Warn("warn log")
}

func TestFilterCaller(t *testing.T) {
logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
log := NewFilter(logger)
_ = log.Log(LevelDebug, "msg1", "te1st debug")
logHelper := NewHelper(NewFilter(logger))
logHelper.Log(LevelDebug, "msg1", "te1st debug")
}

func TestFilterKey(t *testing.T) {
logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
log := NewHelper(NewFilter(logger, FilterKey("password")))
log.Debugw("password", "123456")
}

func TestFilterValue(t *testing.T) {
logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
log := NewHelper(NewFilter(logger, FilterValue("debug")))
log.Debugf("test %s", "debug")
}

func TestFilterFunc(t *testing.T) {
logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
log := NewHelper(NewFilter(logger, FilterFunc(testFilterFunc)))
log.Debug("debug level")
log.Infow("password", "123456")
}

func BenchmarkFilterKey(b *testing.B) {
log := NewHelper(NewFilter(NewStdLogger(io.Discard), FilterKey("password")))
for i := 0; i < b.N; i++ {
log.Infow("password", "123456")
}
}

func BenchmarkFilterValue(b *testing.B) {
log := NewHelper(NewFilter(NewStdLogger(io.Discard), FilterValue("password")))
for i := 0; i < b.N; i++ {
log.Infow("password")
}
}

func BenchmarkFilterFunc(b *testing.B) {
log := NewHelper(NewFilter(NewStdLogger(io.Discard), FilterFunc(testFilterFunc)))
for i := 0; i < b.N; i++ {
log.Info("password", "123456")
}
}

func testFilterFunc(level Level, keyvals ...interface{}) bool {
if level == LevelWarn {
return true
}
for i := 0; i < len(keyvals); i++ {
if keyvals[i] == "password" {
keyvals[i+1] = fuzzyStr
}
}
return false
}

func TestFilterFuncWitchLoggerPrefix(t *testing.T) {
buf := new(bytes.Buffer)
tests := []struct {
logger Logger
want string
}{
{
logger: NewFilter(With(NewStdLogger(buf), "caller", "caller", "prefix", "whaterver"), FilterFunc(testFilterFuncWithLoggerPrefix)),
want: "",
},
{
logger: NewFilter(With(NewStdLogger(buf), "caller", "caller"), FilterFunc(testFilterFuncWithLoggerPrefix)),
want: "INFO caller=caller msg=msg\n",
},
}

for _, tt := range tests {
err := tt.logger.Log(LevelInfo, "msg", "msg")
if err != nil {
t.Fatal("err should be nil")
}
got := buf.String()
if got != tt.want {
t.Fatalf("filter should catch prefix, want %s, got %s.", tt.want, got)
}
buf.Reset()
}
}

func testFilterFuncWithLoggerPrefix(level Level, keyvals ...interface{}) bool {
if level == LevelWarn {
return true
}
for i := 0; i < len(keyvals); i += 2 {
if keyvals[i] == "prefix" {
return true
}
}
return false
}
Loading

0 comments on commit 33b33db

Please sign in to comment.