journal/attr_key.go

139 lines
3.5 KiB
Go
Raw Permalink Normal View History

2024-02-18 10:31:14 +00:00
package journal
import (
"fmt"
)
// AttrKey holds the key part of a key/value (attribute) pair. Passing an
// uninitialised (or invalid) AttrKey to other journal methods will cause the
// attribute to be ignored; check for errors from NewAttrKey to ensure that
// your keys are valid ahead of time.
type AttrKey struct {
// key is written directly to the journal socket. If it is empty, then
// the attribute should be silently discarded. By having it unexported,
// we can ensure that library users can only ever have valid string
// values here.
key string
}
// Key returns the string value of the key. If the attribute is invalid, it
// returns an empty string.
func (ak AttrKey) Key() string {
return ak.key
}
// Valid returns true if ak is valid.
func (ak AttrKey) Valid() bool {
return ak.key != ""
}
// NewAttrKey initialises and returns an AttrKey that may be used when
// constructing key/value (attribute) pairs for logging. If the key is invalid,
// then an error will be returned (and the returned key value will be invalid,
// and any attribute using that key will silently be omitted from the logs).
// Rules for valid attribute keys:
// - must not be empty;
// - must not begin with an underscore '_';
// - must only contain uppercase ASCII 'A''Z', digits '0''9', or
// underscores;
// - must be < 256 characters.
func NewAttrKey(key string) (AttrKey, error) {
if err := AttrKeyValid(key); err != nil {
return AttrKey{}, err
}
return AttrKey{
key: key,
}, nil
}
// MustAttrKey returns an AttrKey for the given string. If the string is not a
// valid systemd attribute key, it returns an invalid AttrKey that will cause
// the attribute not to be emitted to the journal.
func MustAttrKey(key string) AttrKey {
if err := AttrKeyValid(key); err != nil {
return AttrKey{}
}
return AttrKey{
key: key,
}
}
// AttrKeyValid checks the given attribute key and returns an error if there is
// a fault with it.
func AttrKeyValid(key string) error {
switch {
case key == "":
return &InvalidAttrKey{
Key: key,
Reason: AttrKeyEmpty,
}
case key[0] == '_':
return &InvalidAttrKey{
Key: key,
Reason: AttrKeyTrusted,
}
case len(key) >= 256:
return &InvalidAttrKey{
Key: key,
Reason: AttrKeyLength,
}
case !attrKeyValidChars(key):
return &InvalidAttrKey{
Key: key,
Reason: AttrKeyInvalidChars,
}
default:
return nil
}
}
// attrKeyValidChars returns true if the given key comprises only ASCII 'A''Z',
// '0''9' and underscores.
func attrKeyValidChars(key string) bool {
for _, b := range key {
switch {
case b >= 'A' && b <= 'Z':
case b >= '0' && b <= '9':
case b == '_':
default:
return false
}
}
return true
}
// InvalidAttrKey details why a given attribute key is invalid.
type InvalidAttrKey struct {
Key string
Reason InvalidAttrKeyReason
}
func (iak *InvalidAttrKey) Error() string {
return fmt.Sprintf("invalid systemd journal attribute key %q: %s", iak.Key, iak.Reason)
}
type InvalidAttrKeyReason int
const (
AttrKeyEmpty InvalidAttrKeyReason = iota
AttrKeyTrusted
AttrKeyLength
AttrKeyInvalidChars
)
func (iar InvalidAttrKeyReason) String() string {
switch iar {
case AttrKeyEmpty:
return "key is empty"
case AttrKeyTrusted:
return "key is trusted (begins with '_' character)"
case AttrKeyLength:
return "key is too long (max 256 chars)"
case AttrKeyInvalidChars:
return "key contains invalid character (only 'A''Z', '0''9', or '_' allowed)"
default:
return "???"
}
}