Skip to content

Commit

Permalink
Add support for (internal) debug logging output
Browse files Browse the repository at this point in the history
OVERVIEW

The changes provided by this commit were initially intended to provide
a minimal abstraction to support toggling the size of plugin output
off/on as needed for troubleshooting. From there the scope widened as
further iteration showed that it could be useful to add debug logging
support (internally for now) to the library as a whole.

To support toggling on/off specific debug log activity, debug log
output is currently split into two categories:

- debug logging of actions
- debug logging of plugin output size

The intent of these changes is to provide a way to easily toggle
on/off the internal workings of this library. If client code does not
enable debug logging, the result should be this library working just
as it did before.

The current implementation is subject to change as real world testing
is applied and feedback collected.

CHANGES

- allow toggling debug logging output on/off
- allow toggling specific debug logging output on/off
  - e.g., allow all debug messages except for general plugin activity
  - e.g., allow only debug messages for plugin output size calculations
- support setting custom debug logging output target
  - e.g., redirect debug log messages to a target file instead of
    stderr (the default debug log target)
- add test coverage for new behavior

REFERENCES

- refs GH-264
- refs GH-271
  • Loading branch information
atc0005 committed Oct 24, 2024
1 parent 34c382b commit e38b98e
Show file tree
Hide file tree
Showing 8 changed files with 1,375 additions and 78 deletions.
6 changes: 6 additions & 0 deletions exported_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,12 @@ func TestExtractAndDecodeASCII85Payload_FailsToExtractAndDecodePayloadWithInvali
this value is what we're left with:
\x90\xac8 \x04\x9f\xe6\xc2\xfe\x87\x91\x1a\xa6\x85'\xce2
Reproduce via:
fmt.Printf("%+q\n", decodedPayload)
See also:
https://go.dev/blog/strings
This in no way represents the encoded payload nor the original
extracted & decoded payload we would expect to see.
*/
Expand Down
291 changes: 291 additions & 0 deletions logging.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
// Copyright 2024 Adam Chalkley
//
// https://github.com/atc0005/go-nagios
//
// Licensed under the MIT License. See LICENSE file in the project root for
// full license information.

package nagios

import (
"io"
"log"
"os"
"strings"
)

// Logger related values set as constants so that their values are exposed to
// internal tests.
const (
logMsgPrefix string = "[" + MyPackageName + "] "
logFlags int = log.Ldate | log.Ltime

// using log.Lshortfile reports the helper function use instead of the
// caller's location.
//
// logFlags int = log.Ldate | log.Ltime | log.Lshortfile
)

// debugLoggingOptions controls all debug logging behavior for this library.
type debugLoggingOptions struct {
// actions indicates whether actions taken by this library are logged.
// This covers enabling/disabling settings or other general plugin
// activity.
actions bool

// pluginOutputSize indicates whether all output to the configured plugin
// output sink should be measured and written to the log output sink.
pluginOutputSize bool
}

// defaultPluginDebugLoggingOutputTarget returns the default debug logging
// output target used when a user-specified value is not provided.
func defaultPluginDebugLoggingOutputTarget() io.Writer {
return os.Stderr
}

// defaultPluginAbortMessageOutputTarget returns the default abort message
// output target.
func defaultPluginAbortMessageOutputTarget() io.Writer {
return os.Stderr
}

// defaultPluginDebugLoggerTarget returns the default debug logger target used
// when a user-specified value is not provided for the debug output target.
func defaultPluginDebugLoggerTarget() io.Writer {
// The intended default behavior is to throw away debug log messages if a
// debug log message output target has not been specified.
return io.Discard
}

// allDebugLoggingOptionsEnabled is a helper function that provides a
// debugLoggingOptions value with all settings enabled.
func allDebugLoggingOptionsEnabled() debugLoggingOptions {
return debugLoggingOptions{
actions: true,
pluginOutputSize: true,
// Expand this for any new fields added in the future.
}
}

// allDebugLoggingOptionsDisabled is a helper function that provides a
// debugLoggingOptions value with all settings disabled.
func allDebugLoggingOptionsDisabled() debugLoggingOptions {
return debugLoggingOptions{
actions: false,
pluginOutputSize: false,
// Expand this for any new fields added in the future.
}
}

// enableAll enables all debug logging options. The user is able to optionally
// disable select portions of the debug logging output that they do not wish
// to see.
func (dlo *debugLoggingOptions) enableAll() {
*dlo = allDebugLoggingOptionsEnabled()
}

// disableAll disables all debug logging options.
func (dlo *debugLoggingOptions) disableAll() {
*dlo = allDebugLoggingOptionsDisabled()
}

// enableActions enables logging plugin actions.
func (dlo *debugLoggingOptions) enableActions() {
dlo.actions = true
}

// disableActions disables logging plugin actions.
func (dlo *debugLoggingOptions) disableActions() {
dlo.actions = false
}

// enablePluginOutputSize enables logging plugin output size.
func (dlo *debugLoggingOptions) enablePluginOutputSize() {
dlo.pluginOutputSize = true
}

// disablePluginOutputSize disables logging plugin output size.
func (dlo *debugLoggingOptions) disablePluginOutputSize() {
dlo.pluginOutputSize = false
}

// DebugLoggingEnableAll changes the default state of all debug logging
// options for this library from disabled to enabled.
//
// Once enabled, debug logging output is emitted to os.Stderr. This can be
// overridden by explicitly setting a custom debug output target.
func (p *Plugin) DebugLoggingEnableAll() {
// Enable all (granular) debug log options.
p.debugLogging.enableAll()

// Ensure we have a valid output target, but do not overwrite any custom
// target already set.
if p.logOutputSink == nil {
p.setFallbackDebugLogTarget()
}

// Connect logger to configured debug log target.
p.setupLogger()
}

// DebugLoggingDisableAll changes the default state of all debug logging
// options for this library from any custom state back to the default state of
// disabled.
//
// Any custom debug log output target remains as it was before calling this
// function.
func (p *Plugin) DebugLoggingDisableAll() {
p.debugLogging.disableAll()
}

// DebugLoggingDisableActions disables debug logging of general "actions" or
// plugin activity. This is the most verbose debug logging output generated by
// this library.
func (p *Plugin) DebugLoggingDisableActions() {
p.debugLogging.disableActions()
}

// DebugLoggingEnableActions enables debug logging of general "actions" or
// plugin activity. This is the most verbose debug logging output generated by
// this library.
//
// Once enabled, debug logging output is emitted to os.Stderr. This can be
// overridden by explicitly setting a custom debug output target.
func (p *Plugin) DebugLoggingEnableActions() {
p.debugLogging.enableActions()

// Ensure we have a valid output target, but do not overwrite any custom
// target already set.
if p.logOutputSink == nil {
p.setFallbackDebugLogTarget()
}

// Connect logger to configured debug log target.
p.setupLogger()
}

// DebugLoggingDisablePluginOutputSize disables debug logging of plugin output
// size calculations.
func (p *Plugin) DebugLoggingDisablePluginOutputSize() {
p.debugLogging.disablePluginOutputSize()
}

// DebugLoggingEnablePluginOutputSize enables debug logging of plugin output
// size calculations. This debug logging output produces minimal output.
//
// Once enabled, debug logging output is emitted to os.Stderr. This can be
// overridden by explicitly setting a custom debug output target.
func (p *Plugin) DebugLoggingEnablePluginOutputSize() {
p.debugLogging.enablePluginOutputSize()

// Ensure we have a valid output target, but do not overwrite any custom
// target already set.
if p.logOutputSink == nil {
p.setFallbackDebugLogTarget()
}

// Connect logger to configured debug log target.
p.setupLogger()
}

// SetDebugLoggingOutputTarget overrides the current debug logging target with
// the given output target. If the given output target is not valid the
// current target will be used instead. If there isn't a debug logging target
// already set then the default debug logging output target of os.Stderr will
// be used. This behavior is chosen for consistency with the current behavior
// of the Plugin.SetOutputTarget function.
//
// NOTE: While an error message is logged when calling this function with an
// invalid target, calling this function does not change the default debug
// logging state from disabled to enabled. That step must be performed
// separately by either enabling all debug logging options OR enabling select
// debug logging options.
func (p *Plugin) SetDebugLoggingOutputTarget(w io.Writer) {
if w == nil {
if p.logOutputSink == nil {
p.setFallbackDebugLogTarget()
}

// Connect logger to configured debug log target.
p.setupLogger()

// We log using an "unfiltered" logger call to ensure this has the
// best chance of being seen.
p.log("invalid output target provided; using default debug log target instead")

return
}

p.logOutputSink = w

// Connect logger to configured debug log target.
p.setupLogger()

// Use a filtered logger call to allow this message to be emitted or
// excluded based on user-specified debug logging settings.
p.logAction("custom debug logging target set as requested")
}

// DebugLoggingOutputTarget returns the user-specified debug output target or
// the default value if one was not specified.
func (p *Plugin) DebugLoggingOutputTarget() io.Writer {
if p.logOutputSink == nil {
return defaultPluginDebugLoggingOutputTarget()
}

return p.logOutputSink
}

func (p *Plugin) setFallbackDebugLogTarget() {
p.logOutputSink = defaultPluginDebugLoggingOutputTarget()
}

// setupLogger should be called after the debug log output sink is explicitly
// configured. If called before configuring the debug log output sink the
// plugin's default debug logger target will be used instead.
func (p *Plugin) setupLogger() {
var loggerTarget io.Writer
switch {
case p.logOutputSink == nil:
loggerTarget = defaultPluginDebugLoggerTarget()
default:
loggerTarget = p.logOutputSink
}

p.logger = log.New(loggerTarget, logMsgPrefix, logFlags)
}

// log uses the plugin's logger to write the given message to the configured
// output sink.
func (p *Plugin) log(msg string) {
if p.logger == nil {
return
}

if !strings.HasSuffix(msg, CheckOutputEOL) {
msg += CheckOutputEOL
}

p.logger.Print(msg)
}

// logAction is used to log actions taken by this library such as
// enabling/disabling settings or other general plugin activity.
func (p *Plugin) logAction(msg string) {
if !p.debugLogging.actions {
return
}

p.log(msg)
}

// logPluginOutputSize is used to log activity related to measuring all output
// to the configured plugin output sink.
func (p *Plugin) logPluginOutputSize(msg string) {
if !p.debugLogging.pluginOutputSize {
return
}

p.log(msg)
}
12 changes: 12 additions & 0 deletions logging_exported_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright 2024 Adam Chalkley
//
// https://github.com/atc0005/go-nagios
//
// Licensed under the MIT License. See LICENSE file in the project root for
// full license information.

// Package nagios_test provides test coverage for exported package
// functionality.
//
//nolint:dupl,gocognit // ignore "lines are duplicate of" and function complexity
package nagios_test
Loading

0 comments on commit e38b98e

Please sign in to comment.