-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add log output to Windows Event Log #5913
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,21 @@ import ( | |
"go.uber.org/zap/zapcore" | ||
) | ||
|
||
var baseEncodingConfig = zapcore.EncoderConfig{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 on the cleanup |
||
TimeKey: "timestamp", | ||
LevelKey: "level", | ||
NameKey: "logger", | ||
CallerKey: "caller", | ||
MessageKey: "message", | ||
StacktraceKey: "stacktrace", | ||
LineEnding: zapcore.DefaultLineEnding, | ||
EncodeLevel: zapcore.LowercaseLevelEncoder, | ||
EncodeTime: zapcore.ISO8601TimeEncoder, | ||
EncodeDuration: millisecondsDurationEncoder, | ||
EncodeCaller: zapcore.ShortCallerEncoder, | ||
EncodeName: zapcore.FullNameEncoder, | ||
} | ||
|
||
func buildEncoder(cfg Config) zapcore.Encoder { | ||
if cfg.JSON { | ||
return zapcore.NewJSONEncoder(jsonEncoderConfig()) | ||
|
@@ -17,37 +32,14 @@ func buildEncoder(cfg Config) zapcore.Encoder { | |
} | ||
|
||
func jsonEncoderConfig() zapcore.EncoderConfig { | ||
return zapcore.EncoderConfig{ | ||
TimeKey: "timestamp", | ||
LevelKey: "level", | ||
NameKey: "logger", | ||
CallerKey: "caller", | ||
MessageKey: "message", | ||
StacktraceKey: "stacktrace", | ||
LineEnding: zapcore.DefaultLineEnding, | ||
EncodeLevel: zapcore.LowercaseLevelEncoder, | ||
EncodeTime: zapcore.ISO8601TimeEncoder, | ||
EncodeDuration: millisecondsDurationEncoder, | ||
EncodeCaller: zapcore.ShortCallerEncoder, | ||
EncodeName: zapcore.FullNameEncoder, | ||
} | ||
return baseEncodingConfig | ||
} | ||
|
||
func consoleEncoderConfig() zapcore.EncoderConfig { | ||
return zapcore.EncoderConfig{ | ||
TimeKey: "timestamp", | ||
LevelKey: "level", | ||
NameKey: "logger", | ||
CallerKey: "caller", | ||
MessageKey: "message", | ||
StacktraceKey: "stacktrace", | ||
LineEnding: zapcore.DefaultLineEnding, | ||
EncodeLevel: zapcore.CapitalLevelEncoder, | ||
EncodeTime: zapcore.ISO8601TimeEncoder, | ||
EncodeDuration: millisecondsDurationEncoder, | ||
EncodeCaller: zapcore.ShortCallerEncoder, | ||
EncodeName: bracketedNameEncoder, | ||
} | ||
c := baseEncodingConfig | ||
c.EncodeLevel = zapcore.CapitalLevelEncoder | ||
c.EncodeName = bracketedNameEncoder | ||
return c | ||
} | ||
|
||
func syslogEncoderConfig() zapcore.EncoderConfig { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// +build !windows | ||
|
||
package logp | ||
|
||
import ( | ||
"github.com/pkg/errors" | ||
"go.uber.org/zap/zapcore" | ||
) | ||
|
||
func newEventLog(beatname string, encoder zapcore.Encoder, enab zapcore.LevelEnabler) (zapcore.Core, error) { | ||
return nil, errors.New("eventlog is only supported on Windows") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package logp | ||
|
||
import ( | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
"go.uber.org/zap/zapcore" | ||
"golang.org/x/sys/windows/svc/eventlog" | ||
) | ||
|
||
const ( | ||
// eventID is arbitrary but must be between [1-1000]. | ||
eventID = 100 | ||
supports = eventlog.Error | eventlog.Warning | eventlog.Info | ||
) | ||
|
||
const alreadyExistsMsg = "registry key already exists" | ||
|
||
type eventLogCore struct { | ||
zapcore.LevelEnabler | ||
encoder zapcore.Encoder | ||
fields []zapcore.Field | ||
log *eventlog.Log | ||
} | ||
|
||
func newEventLog(appName string, encoder zapcore.Encoder, enab zapcore.LevelEnabler) (zapcore.Core, error) { | ||
if appName == "" { | ||
return nil, errors.New("appName cannot be empty") | ||
} | ||
appName = strings.Title(strings.ToLower(appName)) | ||
|
||
if err := eventlog.InstallAsEventCreate(appName, supports); err != nil { | ||
if !strings.Contains(err.Error(), alreadyExistsMsg) { | ||
return nil, errors.Wrap(err, "failed to setup eventlog") | ||
} | ||
} | ||
|
||
log, err := eventlog.Open(appName) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "failed to open eventlog") | ||
} | ||
|
||
return &eventLogCore{ | ||
LevelEnabler: enab, | ||
encoder: encoder, | ||
log: log, | ||
}, nil | ||
} | ||
|
||
func (c *eventLogCore) With(fields []zapcore.Field) zapcore.Core { | ||
clone := c.Clone() | ||
clone.fields = append(clone.fields, fields...) | ||
return clone | ||
} | ||
|
||
func (c *eventLogCore) Check(entry zapcore.Entry, checked *zapcore.CheckedEntry) *zapcore.CheckedEntry { | ||
if c.Enabled(entry.Level) { | ||
return checked.AddCore(entry, c) | ||
} | ||
return checked | ||
} | ||
|
||
func (c *eventLogCore) Write(entry zapcore.Entry, fields []zapcore.Field) error { | ||
buffer, err := c.encoder.EncodeEntry(entry, fields) | ||
if err != nil { | ||
return errors.Wrap(err, "failed to encode entry") | ||
} | ||
|
||
msg := buffer.String() | ||
switch entry.Level { | ||
case zapcore.DebugLevel, zapcore.InfoLevel: | ||
return c.log.Info(eventID, msg) | ||
case zapcore.WarnLevel: | ||
return c.log.Warning(eventID, msg) | ||
case zapcore.ErrorLevel, zapcore.DPanicLevel, zapcore.PanicLevel, zapcore.FatalLevel: | ||
return c.log.Error(eventID, msg) | ||
default: | ||
return errors.Errorf("unhandled log level: %v", entry.Level) | ||
} | ||
} | ||
|
||
func (c *eventLogCore) Sync() error { | ||
return nil | ||
} | ||
|
||
func (c *eventLogCore) Clone() *eventLogCore { | ||
clone := *c | ||
clone.encoder = c.encoder.Clone() | ||
clone.fields = make([]zapcore.Field, len(c.fields), len(c.fields)+10) | ||
copy(clone.fields, c.fields) | ||
return &clone | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,7 +53,7 @@ func (c *syslogCore) Write(entry zapcore.Entry, fields []zapcore.Field) error { | |
} | ||
|
||
// Console encoder writes tabs which don't render nicely with syslog. | ||
replaceTabsWithSpaces(buffer.Bytes(), 2) | ||
replaceTabsWithSpaces(buffer.Bytes(), 4) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that change related to this PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not related. I just realized that there can be up to 4 tabs. |
||
|
||
msg := buffer.String() | ||
switch entry.Level { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a big fan of naming we have for historical reason and I'm thinking we should change it for 7.0. Perhaps we could already follow here a new scheme similar to what we have in other places:
logging.eventlog.enabled
. Like this we can have specific logging options under the namespace.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am 100% on board with changing how logging is configured for 7.0. I did it this way to be consistent. I will open an issue to track things that I'd like to "break" for 7.0 (I have a list 😄 ).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be possible to already do the "new" scheme here as so far it's just a name. More thinking then we have to break one option less for 7 :-) But not sure if we are already settle on if that should be new scheme.
+1 on the issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well the scheme I had in mind kind of conflicts. I was thinking of something like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
got it, then lets keep it as is in your PR and break it later so we don't need to come to a conclusion now.
I like your format as it gives max flexibility. I assume you have in mind that more then 1 log output can be enabled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, more than one output and possibly more than one instance of an output type (not 100% sure yet; requires more thought).