Initial implementation #3
6 changed files with 452 additions and 0 deletions
56
.golangci.yml
Normal file
56
.golangci.yml
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# This file contains all available configuration options
|
||||||
|
# with their default values.
|
||||||
|
|
||||||
|
# options for analysis running
|
||||||
|
run:
|
||||||
|
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||||
|
timeout: 5m
|
||||||
|
|
||||||
|
# include test files or not, default is true
|
||||||
|
tests: true
|
||||||
|
|
||||||
|
# output configuration options
|
||||||
|
output:
|
||||||
|
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
||||||
|
format: tab
|
||||||
|
|
||||||
|
# all available settings of specific linters
|
||||||
|
linters-settings:
|
||||||
|
exhaustive:
|
||||||
|
# indicates that switch statements are to be considered exhaustive if a
|
||||||
|
# 'default' case is present, even if all enum members aren't listed in the
|
||||||
|
# switch
|
||||||
|
default-signifies-exhaustive: true
|
||||||
|
govet:
|
||||||
|
# report about shadowed variables
|
||||||
|
check-shadowing: false
|
||||||
|
|
||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
# mandatory linters
|
||||||
|
- govet
|
||||||
|
- revive
|
||||||
|
|
||||||
|
# some default golangci-lint linters
|
||||||
|
- errcheck
|
||||||
|
- gosimple
|
||||||
|
- ineffassign
|
||||||
|
- staticcheck
|
||||||
|
- typecheck
|
||||||
|
- unused
|
||||||
|
|
||||||
|
# extra linters
|
||||||
|
- exhaustive
|
||||||
|
- godot
|
||||||
|
- gofmt
|
||||||
|
- whitespace
|
||||||
|
- goimports
|
||||||
|
disable-all: true
|
||||||
|
fast: false
|
||||||
|
|
||||||
|
issues:
|
||||||
|
include:
|
||||||
|
- EXC0002 # should have a comment
|
||||||
|
- EXC0003 # test/Test ... consider calling this
|
||||||
|
- EXC0004 # govet
|
||||||
|
- EXC0005 # C-style breaks
|
115
README.md
Normal file
115
README.md
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
# Zap Core for systemd Journal
|
||||||
|
Package `zapjournld` provides `zap` Core for systemd Journal. It supports structured logging.
|
||||||
|
|
||||||
|
Applications may use zap logger to write logs directly into journald and may relatively freely define additional fields, which will be indexed by journald.
|
||||||
|
|
||||||
|
Information about common and custom journald fields is available on https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html.
|
||||||
|
## Example
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/zapjournald"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/ssgreg/journald"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// StandardLogger
|
||||||
|
standardLogger, _ := NewStandardLogger("info")
|
||||||
|
|
||||||
|
standardLogger.Info("Simple log raw 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStandardLogger(lvlStr string) (*zap.Logger, zap.AtomicLevel) {
|
||||||
|
lvl, err := getLogLevel(lvlStr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
zc := zap.NewProductionConfig()
|
||||||
|
zc.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||||
|
zc.Level = zap.NewAtomicLevelAt(lvl)
|
||||||
|
|
||||||
|
// Initialize Zap.
|
||||||
|
encoder := zapcore.NewJSONEncoder(zc.EncoderConfig)
|
||||||
|
|
||||||
|
core := zapjournald.NewCore(zap.NewAtomicLevelAt(lvl), encoder, &journald.Journal{}, zapjournald.SyslogFields)
|
||||||
|
coreWithContext := core.With([]zapcore.Field{
|
||||||
|
zapjournald.SyslogFacility(zapjournald.LogDaemon),
|
||||||
|
zapjournald.SyslogIdentifier(),
|
||||||
|
zapjournald.SyslogPid(),
|
||||||
|
})
|
||||||
|
l := zap.New(coreWithContext, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)))
|
||||||
|
return l, zc.Level
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLogLevel(lvlStr string) (zapcore.Level, error) {
|
||||||
|
var lvl zapcore.Level
|
||||||
|
err := lvl.UnmarshalText([]byte(lvlStr))
|
||||||
|
if err != nil {
|
||||||
|
return lvl, fmt.Errorf("incorrect logger level configuration %s (%v), "+
|
||||||
|
"value should be one of %v", lvlStr, err, [...]zapcore.Level{
|
||||||
|
zapcore.DebugLevel,
|
||||||
|
zapcore.InfoLevel,
|
||||||
|
zapcore.WarnLevel,
|
||||||
|
zapcore.ErrorLevel,
|
||||||
|
zapcore.DPanicLevel,
|
||||||
|
zapcore.PanicLevel,
|
||||||
|
zapcore.FatalLevel,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return lvl, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Results
|
||||||
|
|
||||||
|
#### You can read simple view like this:
|
||||||
|
```
|
||||||
|
sudo journalctl -f
|
||||||
|
Apr 07 13:06:34 user ___11go_build_git_frostfs_info_TrueCloudLab_zapjournald_main[330983]: {"level":"info","ts":"2023-04-07T13:06:34.990+0300","msg":"Simple log raw 1","SYSLOG_FACILITY":3,"SYSLOG_IDENTIFIER":"___11go_build_git_frostfs_info_TrueCloudLab_zapjournald_main","SYSLOG_PID":330983}
|
||||||
|
```
|
||||||
|
#### Or you can find lines by indexed field
|
||||||
|
```
|
||||||
|
sudo journalctl SYSLOG_PID=76385
|
||||||
|
Apr 07 13:06:34 user ___11go_build_git_frostfs_info_TrueCloudLab_zapjournald_main[330983]: {"level":"info","ts":"2023-04-07T13:06:34.990+0300","msg":"Simple log raw 1","SYSLOG_FACILITY":3,"SYSLOG_ID>
|
||||||
|
```
|
||||||
|
#### Or you can read full-structured view like this:
|
||||||
|
```
|
||||||
|
sudo journalctl SYSLOG_PID=76385 --output=json-pretty
|
||||||
|
{
|
||||||
|
"PRIORITY" : "6",
|
||||||
|
"_SYSTEMD_OWNER_UID" : "1000",
|
||||||
|
"_HOSTNAME" : "user",
|
||||||
|
"__MONOTONIC_TIMESTAMP" : "153798818198",
|
||||||
|
"_SYSTEMD_SLICE" : "user-1000.slice",
|
||||||
|
"_GID" : "1000",
|
||||||
|
"_AUDIT_LOGINUID" : "1000",
|
||||||
|
"_UID" : "1000",
|
||||||
|
"SYSLOG_IDENTIFIER" : "___11go_build_git_frostfs_info_TrueCloudLab_zapjournald_main",
|
||||||
|
"__REALTIME_TIMESTAMP" : "1680861994990646",
|
||||||
|
"_CAP_EFFECTIVE" : "0",
|
||||||
|
"_COMM" : "___11go_build_g",
|
||||||
|
"_AUDIT_SESSION" : "59",
|
||||||
|
"__CURSOR" : "s=9e2d157a286a437ea3405618239c1e07;i=14a96;b=bc1bc632f462476abc9e3e3b0c517f6c;m=23cf1fb996;t=5f8bc2e20bc36;x=9c6c4a86b6da43c",
|
||||||
|
"_SYSTEMD_USER_SLICE" : "app.slice",
|
||||||
|
"_SYSTEMD_CGROUP" : "/user.slice/user-1000.slice/user@1000.service/app.slice/app-gnome-jetbrains\\x2dgoland-203587.scope",
|
||||||
|
"MESSAGE" : "{\"level\":\"info\",\"ts\":\"2023-04-07T13:06:34.990+0300\",\"msg\":\"Simple log raw 1\",\"SYSLOG_FACILITY\":3,\"SYSLOG_IDENTIFIER\":\"___11go_build_git_frostfs_info_TrueCloudLab_zapjournal>
|
||||||
|
"_SOURCE_REALTIME_TIMESTAMP" : "1680861994990539",
|
||||||
|
"_MACHINE_ID" : "82be99c92deb4b36850366d7742de698",
|
||||||
|
"SYSLOG_FACILITY" : "3",
|
||||||
|
"_BOOT_ID" : "bc1bc632f462476abc9e3e3b0c517f6c",
|
||||||
|
"_PID" : "330983",
|
||||||
|
"_SELINUX_CONTEXT" : "unconfined\n",
|
||||||
|
"_SYSTEMD_UNIT" : "user@1000.service",
|
||||||
|
"_TRANSPORT" : "journal",
|
||||||
|
"_SYSTEMD_INVOCATION_ID" : "ed23dc57a96340029ef8bf9956182c20",
|
||||||
|
"_SYSTEMD_USER_UNIT" : "app-gnome-jetbrains\\x2dgoland-203587.scope",
|
||||||
|
"SYSLOG_PID" : "330983"
|
||||||
|
}
|
||||||
|
```
|
77
fields.go
Normal file
77
fields.go
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package zapjournald
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Facility defines syslog facility from /usr/include/sys/syslog.h .
|
||||||
|
type Facility uint32
|
||||||
|
|
||||||
|
// Syslog compatibility fields.
|
||||||
|
const (
|
||||||
|
SyslogFacilityField = "SYSLOG_FACILITY"
|
||||||
|
SyslogIdentifierField = "SYSLOG_IDENTIFIER"
|
||||||
|
SyslogPidField = "SYSLOG_PID"
|
||||||
|
SyslogTimestampField = "SYSLOG_TIMESTAMP"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Facilities before LogFtp are the same on Linux, BSD, and OS X.
|
||||||
|
const (
|
||||||
|
LogKern Facility = iota << 3
|
||||||
|
LogUser
|
||||||
|
LogMail
|
||||||
|
LogDaemon
|
||||||
|
LogAuth
|
||||||
|
LogSyslog
|
||||||
|
LogLpr
|
||||||
|
LogNews
|
||||||
|
LogUucp
|
||||||
|
LogCron
|
||||||
|
LogAuthpriv
|
||||||
|
LogFtp
|
||||||
|
_ // unused
|
||||||
|
LogAudit // unused
|
||||||
|
_ // unused
|
||||||
|
_ // unused
|
||||||
|
LogLocal0
|
||||||
|
LogLocal1
|
||||||
|
LogLocal2
|
||||||
|
LogLocal3
|
||||||
|
LogLocal4
|
||||||
|
LogLocal5
|
||||||
|
LogLocal6
|
||||||
|
LogLocal7
|
||||||
|
)
|
||||||
|
|
||||||
|
// LogFacmask is used to extract facility part of the message.
|
||||||
|
const LogFacmask = 0x03f8
|
||||||
|
|
||||||
|
// SyslogFields contains slice of fields that
|
||||||
|
|||||||
|
// indexed with syslog by default.
|
||||||
|
var SyslogFields = []string{
|
||||||
|
SyslogFacilityField,
|
||||||
|
SyslogIdentifierField,
|
||||||
|
SyslogPidField,
|
||||||
|
SyslogTimestampField,
|
||||||
|
}
|
||||||
|
|
||||||
|
func SyslogFacility(facility Facility) zapcore.Field {
|
||||||
|
return zap.Uint32(SyslogFacilityField, uint32(facility&LogFacmask)>>3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SyslogIdentifier() zapcore.Field {
|
||||||
|
return zap.String(SyslogIdentifierField, filepath.Base(os.Args[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func SyslogPid() zapcore.Field {
|
||||||
|
return zap.Int(SyslogPidField, os.Getpid())
|
||||||
|
}
|
||||||
|
|
||||||
|
func SyslogTimestamp(time time.Time) zapcore.Field {
|
||||||
|
return zap.Time(SyslogTimestampField, time)
|
||||||
|
}
|
12
go.mod
12
go.mod
|
@ -1,3 +1,15 @@
|
||||||
module git.frostfs.info/TrueCloudLab/zapjournald
|
module git.frostfs.info/TrueCloudLab/zapjournald
|
||||||
|
|
||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/ssgreg/journald v1.0.0
|
||||||
|
go.uber.org/zap v1.24.0
|
||||||
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
|
golang.org/x/sys v0.1.0 // indirect
|
||||||
|
)
|
||||||
|
|
24
go.sum
Normal file
24
go.sum
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
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/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/ssgreg/journald v1.0.0 h1:0YmTDPJXxcWDPba12qNMdO6TxvfkFSYpFIJ31CwmLcU=
|
||||||
|
github.com/ssgreg/journald v1.0.0/go.mod h1:RUckwmTM8ghGWPslq2+ZBZzbb9/2KgjzYZ4JEP+oRt0=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||||
|
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||||
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||||
|
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||||
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
|
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||||
|
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||||
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
||||||
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
|
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||||
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
168
zapjournald.go
Normal file
168
zapjournald.go
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
package zapjournald
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ssgreg/journald"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Core for zapjournald.
|
||||||
|
//
|
||||||
|
// Implements zapcore.LevelEnabler and zapcore.Core interfaces.
|
||||||
|
type Core struct {
|
||||||
|
zapcore.LevelEnabler
|
||||||
|
encoder zapcore.Encoder
|
||||||
|
j *journald.Journal
|
||||||
|
// field names, which will be stored in journald structure
|
||||||
|
storedFieldNames map[string]struct{}
|
||||||
|
// journald fields, which are always present in current core context
|
||||||
|
contextStructuredFields map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCore(enab zapcore.LevelEnabler, encoder zapcore.Encoder, journal *journald.Journal, journalFields []string) *Core {
|
||||||
|
journalFieldsMap := make(map[string]struct{})
|
||||||
|
for _, field := range journalFields {
|
||||||
|
journalFieldsMap[field] = struct{}{}
|
||||||
|
}
|
||||||
|
return &Core{
|
||||||
|
LevelEnabler: enab,
|
||||||
|
encoder: encoder,
|
||||||
|
j: journal,
|
||||||
|
storedFieldNames: journalFieldsMap,
|
||||||
|
contextStructuredFields: make(map[string]interface{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// With adds structured context to the Core.
|
||||||
|
func (core *Core) With(fields []zapcore.Field) zapcore.Core {
|
||||||
|
clone := core.clone()
|
||||||
|
for _, field := range fields {
|
||||||
|
field.AddTo(clone.encoder)
|
||||||
|
clone.contextStructuredFields[field.Key] = getFieldValue(field)
|
||||||
|
}
|
||||||
|
|
||||||
|
return clone
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check determines whether the supplied Entry should be logged (using the
|
||||||
|
// embedded LevelEnabler and possibly some extra logic). If the entry
|
||||||
|
// should be logged, the Core adds itself to the CheckedEntry and returns
|
||||||
|
// the result.
|
||||||
|
//
|
||||||
|
// Callers must use Check before calling Write.
|
||||||
|
func (core *Core) Check(entry zapcore.Entry, checked *zapcore.CheckedEntry) *zapcore.CheckedEntry {
|
||||||
|
if core.Enabled(entry.Level) {
|
||||||
|
return checked.AddCore(entry, core)
|
||||||
|
}
|
||||||
|
return checked
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write serializes the Entry and any Fields supplied at the log site and
|
||||||
|
// writes them to their destination.
|
||||||
|
//
|
||||||
|
// If called, Write should always log the Entry and Fields; it should not
|
||||||
|
// replicate the logic of Check.
|
||||||
|
func (core *Core) Write(entry zapcore.Entry, fields []zapcore.Field) error {
|
||||||
|
// Generate the message.
|
||||||
|
buffer, err := core.encoder.EncodeEntry(entry, fields)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to encode log entry: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
message := buffer.String()
|
||||||
|
|
||||||
|
structuredFields := maps.Clone(core.contextStructuredFields)
|
||||||
|
for _, field := range fields {
|
||||||
|
if _, isJournalField := core.storedFieldNames[field.Key]; isJournalField {
|
||||||
|
structuredFields[field.Key] = getFieldValue(field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the message.
|
||||||
|
switch entry.Level {
|
||||||
|
case zapcore.DebugLevel:
|
||||||
|
return core.j.Send(message, journald.PriorityDebug, structuredFields)
|
||||||
|
|
||||||
|
case zapcore.InfoLevel:
|
||||||
|
return core.j.Send(message, journald.PriorityInfo, structuredFields)
|
||||||
|
|
||||||
|
case zapcore.WarnLevel:
|
||||||
|
return core.j.Send(message, journald.PriorityWarning, structuredFields)
|
||||||
|
|
||||||
|
case zapcore.ErrorLevel:
|
||||||
|
return core.j.Send(message, journald.PriorityErr, structuredFields)
|
||||||
|
|
||||||
|
case zapcore.DPanicLevel:
|
||||||
|
return core.j.Send(message, journald.PriorityCrit, structuredFields)
|
||||||
|
|
||||||
|
case zapcore.PanicLevel:
|
||||||
|
return core.j.Send(message, journald.PriorityCrit, structuredFields)
|
||||||
|
|
||||||
|
case zapcore.FatalLevel:
|
||||||
|
return core.j.Send(message, journald.PriorityCrit, structuredFields)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown log level: %v", entry.Level)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync flushes buffered logs (not used).
|
||||||
|
func (core *Core) Sync() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone returns clone of core.
|
||||||
|
func (core *Core) clone() *Core {
|
||||||
|
return &Core{
|
||||||
|
LevelEnabler: core.LevelEnabler,
|
||||||
|
encoder: core.encoder.Clone(),
|
||||||
|
j: core.j,
|
||||||
|
storedFieldNames: maps.Clone(core.storedFieldNames),
|
||||||
|
contextStructuredFields: maps.Clone(core.contextStructuredFields),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getFieldValue returns underlying value stored in zapcore.Field.
|
||||||
|
func getFieldValue(f zapcore.Field) interface{} {
|
||||||
|
switch f.Type {
|
||||||
|
case zapcore.ArrayMarshalerType,
|
||||||
|
zapcore.ObjectMarshalerType,
|
||||||
|
zapcore.InlineMarshalerType,
|
||||||
|
zapcore.BinaryType,
|
||||||
|
zapcore.BoolType,
|
||||||
|
zapcore.ByteStringType,
|
||||||
|
zapcore.Complex128Type,
|
||||||
|
zapcore.Complex64Type,
|
||||||
|
zapcore.TimeFullType,
|
||||||
|
zapcore.ReflectType,
|
||||||
|
zapcore.NamespaceType,
|
||||||
|
zapcore.StringerType,
|
||||||
|
zapcore.ErrorType,
|
||||||
|
zapcore.SkipType:
|
||||||
|
return f.Interface
|
||||||
|
case zapcore.DurationType,
|
||||||
|
zapcore.Float64Type,
|
||||||
|
zapcore.Float32Type,
|
||||||
|
zapcore.Int64Type,
|
||||||
|
zapcore.Int32Type,
|
||||||
|
zapcore.Int16Type,
|
||||||
|
zapcore.Int8Type,
|
||||||
|
zapcore.Uint64Type,
|
||||||
|
zapcore.Uint32Type,
|
||||||
|
zapcore.Uint16Type,
|
||||||
|
zapcore.Uint8Type,
|
||||||
|
zapcore.UintptrType:
|
||||||
|
return f.Integer
|
||||||
|
case zapcore.StringType:
|
||||||
|
return f.String
|
||||||
|
case zapcore.TimeType:
|
||||||
|
if f.Interface != nil {
|
||||||
|
return f.Interface
|
||||||
|
}
|
||||||
|
return f.Integer
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unknown field type: %v", f))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue
->