mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-02-17 18:26:36 +00:00
124
pkg/log/eventlog/eventlog.go
Normal file
124
pkg/log/eventlog/eventlog.go
Normal file
@@ -0,0 +1,124 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
// Package eventlog provides a Logger that writes to Windows Event Log.
|
||||
package eventlog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"golang.org/x/sys/windows"
|
||||
goeventlog "golang.org/x/sys/windows/svc/eventlog"
|
||||
)
|
||||
|
||||
const (
|
||||
// NeLogOemCode is a generic error log entry for OEMs to use to
|
||||
// elog errors from OEM value added services.
|
||||
// See: https://github.com/microsoft/win32metadata/blob/2f3c5282ce1024a712aeccd90d3aa50bf7a49e27/generation/WinSDK/RecompiledIdlHeaders/um/LMErrlog.h#L824-L845
|
||||
neLogOemCode = uint32(3299)
|
||||
)
|
||||
|
||||
type Priority struct {
|
||||
etype int
|
||||
}
|
||||
|
||||
// NewEventLogLogger returns a new Logger which writes to Windows EventLog in event log format.
|
||||
// The body of the log message is the formatted output from the Logger returned
|
||||
// by newLogger.
|
||||
func NewEventLogLogger(w *goeventlog.Log, newLogger func(io.Writer) log.Logger) log.Logger {
|
||||
l := &eventlogLogger{
|
||||
w: w,
|
||||
newLogger: newLogger,
|
||||
prioritySelector: defaultPrioritySelector,
|
||||
bufPool: sync.Pool{New: func() interface{} {
|
||||
return &loggerBuf{}
|
||||
}},
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
type eventlogLogger struct {
|
||||
w *goeventlog.Log
|
||||
newLogger func(io.Writer) log.Logger
|
||||
prioritySelector PrioritySelector
|
||||
bufPool sync.Pool
|
||||
}
|
||||
|
||||
func (l *eventlogLogger) Log(keyvals ...interface{}) error {
|
||||
priority := l.prioritySelector(keyvals...)
|
||||
|
||||
lb := l.getLoggerBuf()
|
||||
defer l.putLoggerBuf(lb)
|
||||
if err := lb.logger.Log(keyvals...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// golang.org/x/sys/windows/svc/eventlog does not provide func which allows to send more than one string.
|
||||
// See: https://github.com/golang/go/issues/59780
|
||||
|
||||
msg, err := syscall.UTF16PtrFromString(lb.buf.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error convert string to UTF-16: %v", err)
|
||||
}
|
||||
|
||||
ss := []*uint16{msg, nil, nil, nil, nil, nil, nil, nil, nil}
|
||||
return windows.ReportEvent(l.w.Handle, uint16(priority.etype), 0, neLogOemCode, 0, 9, 0, &ss[0], nil)
|
||||
}
|
||||
|
||||
type loggerBuf struct {
|
||||
buf *bytes.Buffer
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func (l *eventlogLogger) getLoggerBuf() *loggerBuf {
|
||||
lb := l.bufPool.Get().(*loggerBuf)
|
||||
if lb.buf == nil {
|
||||
lb.buf = &bytes.Buffer{}
|
||||
lb.logger = l.newLogger(lb.buf)
|
||||
} else {
|
||||
lb.buf.Reset()
|
||||
}
|
||||
return lb
|
||||
}
|
||||
|
||||
func (l *eventlogLogger) putLoggerBuf(lb *loggerBuf) {
|
||||
l.bufPool.Put(lb)
|
||||
}
|
||||
|
||||
// PrioritySelector inspects the list of keyvals and selects an eventlog priority.
|
||||
type PrioritySelector func(keyvals ...interface{}) Priority
|
||||
|
||||
// defaultPrioritySelector convert a kit/log level into a Windows Eventlog level
|
||||
func defaultPrioritySelector(keyvals ...interface{}) Priority {
|
||||
l := len(keyvals)
|
||||
|
||||
eType := windows.EVENTLOG_SUCCESS
|
||||
|
||||
for i := 0; i < l; i += 2 {
|
||||
if keyvals[i] == level.Key() {
|
||||
var val interface{}
|
||||
if i+1 < l {
|
||||
val = keyvals[i+1]
|
||||
}
|
||||
if v, ok := val.(level.Value); ok {
|
||||
switch v {
|
||||
case level.ErrorValue():
|
||||
eType = windows.EVENTLOG_ERROR_TYPE
|
||||
case level.WarnValue():
|
||||
eType = windows.EVENTLOG_WARNING_TYPE
|
||||
case level.InfoValue():
|
||||
eType = windows.EVENTLOG_INFORMATION_TYPE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Priority{etype: eType}
|
||||
}
|
||||
43
pkg/log/flag/flag.go
Normal file
43
pkg/log/flag/flag.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2017 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package flag
|
||||
|
||||
import (
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/log"
|
||||
"github.com/prometheus/common/promlog"
|
||||
promlogflag "github.com/prometheus/common/promlog/flag"
|
||||
)
|
||||
|
||||
// FileFlagName is the canonical flag name to configure the log file
|
||||
const FileFlagName = "log.file"
|
||||
|
||||
// FileFlagHelp is the help description for the log.file flag.
|
||||
const FileFlagHelp = "Output file of log messages. One of [stdout, stderr, eventlog, <path to log file>]"
|
||||
|
||||
// AddFlags adds the flags used by this package to the Kingpin application.
|
||||
// To use the default Kingpin application, call AddFlags(kingpin.CommandLine)
|
||||
func AddFlags(a *kingpin.Application, config *log.Config) {
|
||||
config.Level = &promlog.AllowedLevel{}
|
||||
a.Flag(promlogflag.LevelFlagName, promlogflag.LevelFlagHelp).
|
||||
Default("info").SetValue(config.Level)
|
||||
|
||||
config.File = &log.AllowedFile{}
|
||||
a.Flag(FileFlagName, FileFlagHelp).
|
||||
Default("stderr").SetValue(config.File)
|
||||
|
||||
config.Format = &promlog.AllowedFormat{}
|
||||
a.Flag(promlogflag.FormatFlagName, promlogflag.FormatFlagHelp).
|
||||
Default("logfmt").SetValue(config.Format)
|
||||
}
|
||||
93
pkg/log/logger.go
Normal file
93
pkg/log/logger.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/log/eventlog"
|
||||
"github.com/prometheus/common/promlog"
|
||||
goeventlog "golang.org/x/sys/windows/svc/eventlog"
|
||||
)
|
||||
|
||||
// AllowedFile is a settable identifier for the output file that the logger can have.
|
||||
type AllowedFile struct {
|
||||
s string
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (f *AllowedFile) String() string {
|
||||
return f.s
|
||||
}
|
||||
|
||||
// Set updates the value of the allowed format.
|
||||
func (f *AllowedFile) Set(s string) error {
|
||||
f.s = s
|
||||
switch s {
|
||||
case "stdout":
|
||||
f.w = os.Stdout
|
||||
case "stderr":
|
||||
f.w = os.Stderr
|
||||
case "eventlog":
|
||||
f.w = nil
|
||||
default:
|
||||
file, err := os.OpenFile(s, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0200)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.w = file
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Config is a struct containing configurable settings for the logger
|
||||
type Config struct {
|
||||
promlog.Config
|
||||
|
||||
File *AllowedFile
|
||||
}
|
||||
|
||||
func New(config *Config) (log.Logger, error) {
|
||||
if config.File == nil {
|
||||
return nil, errors.New("log file undefined")
|
||||
}
|
||||
|
||||
if config.Format == nil {
|
||||
return nil, errors.New("log format undefined")
|
||||
}
|
||||
|
||||
var (
|
||||
l log.Logger
|
||||
loggerFunc func(io.Writer) log.Logger
|
||||
)
|
||||
|
||||
switch config.Format.String() {
|
||||
case "json":
|
||||
loggerFunc = log.NewJSONLogger
|
||||
case "logfmt":
|
||||
loggerFunc = log.NewLogfmtLogger
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported log.format %q", config.Format.String())
|
||||
}
|
||||
|
||||
if config.File.s == "eventlog" {
|
||||
w, err := goeventlog.Open("windows_exporter")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l = eventlog.NewEventLogLogger(w, loggerFunc)
|
||||
} else if config.File.w == nil {
|
||||
panic("logger: file writer is nil")
|
||||
} else {
|
||||
l = loggerFunc(log.NewSyncWriter(config.File.w))
|
||||
}
|
||||
|
||||
promlogConfig := promlog.Config{
|
||||
Format: config.Format,
|
||||
Level: config.Level,
|
||||
}
|
||||
|
||||
return promlog.NewWithLogger(l, &promlogConfig), nil
|
||||
}
|
||||
Reference in New Issue
Block a user