prometheus/util/logging/file.go
TJ Hoplock e0104a6b7e ref: JSONFileLogger slog handler, add scrape.FailureLogger interface
Improves upon #15434, better resolves #15433.

This commit introduces a few changes to ensure safer handling of the
JSONFileLogger:
- the JSONFileLogger struct now implements the slog.Handler interface,
  so it can directly be used to create slog Loggers. This pattern more
closely aligns with upstream slog usage (which is generally based around
handlers), as well as making it clear that devs are creating a whole new
logger when interacting with it (vs silently modifying internal configs
like it did previously).
- updates the `promql.QueryLogger` interface to be a union of the
  methods of both the `io.Closer` interface and the `slog.Handler`
interface. This allows for plugging in/using slog-compatible loggers
other than the JSONFileLogger, if desired (ie, for downstream projects).
- introduces new `scrape.FailureLogger` interface; just like
  `promql.QueryLogger`, it is a union of `io.Closer` and `slog.Handler`
interfaces. Similar logic applies to reasoning.
- updates tests where needed; have the `FakeQueryLogger` from promql's
  engine_test implement the `slog.Handler`, improve JSONFileLogger test
suite, etc.

Signed-off-by: TJ Hoplock <t.hoplock@gmail.com>
2024-11-28 23:14:31 -05:00

95 lines
2.9 KiB
Go

// Copyright 2020 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 logging
import (
"context"
"fmt"
"io"
"log/slog"
"os"
"github.com/prometheus/common/promslog"
)
var _ slog.Handler = (*JSONFileLogger)(nil)
var _ io.Closer = (*JSONFileLogger)(nil)
// JSONFileLogger represents a logger that writes JSON to a file. It implements
// the slog.Handler interface, as well as the io.Closer interface.
type JSONFileLogger struct {
handler slog.Handler
file *os.File
}
// NewJSONFileLogger returns a new JSONFileLogger.
func NewJSONFileLogger(s string) (*JSONFileLogger, error) {
if s == "" {
return nil, nil
}
f, err := os.OpenFile(s, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o666)
if err != nil {
return nil, fmt.Errorf("can't create json log file: %w", err)
}
jsonFmt := &promslog.AllowedFormat{}
_ = jsonFmt.Set("json")
return &JSONFileLogger{
handler: promslog.New(&promslog.Config{Format: jsonFmt, Writer: f}).Handler(),
file: f,
}, nil
}
// Close closes the underlying file. It implements the io.Closer interface.
func (l *JSONFileLogger) Close() error {
return l.file.Close()
}
// Enabled returns true if and only if the internal slog.Handler is enabled. It
// implements the slog.Handler interface.
func (l *JSONFileLogger) Enabled(ctx context.Context, level slog.Level) bool {
return l.handler.Enabled(ctx, level)
}
// Handle takes record created by an slog.Logger and forwards it to the
// internal slog.Handler for dispatching the log call to the backing file. It
// implements the slog.Handler interface.
func (l *JSONFileLogger) Handle(ctx context.Context, r slog.Record) error {
return l.handler.Handle(ctx, r.Clone())
}
// WithAttrs returns a new *JSONFileLogger with a new internal handler that has
// the provided attrs attached as attributes on all further log calls. It
// implements the slog.Handler interface.
func (l *JSONFileLogger) WithAttrs(attrs []slog.Attr) slog.Handler {
if len(attrs) == 0 {
return l
}
return &JSONFileLogger{file: l.file, handler: l.handler.WithAttrs(attrs)}
}
// WithGroup returns a new *JSONFileLogger with a new internal handler that has
// the provided group name attached, to group all other attributes added to the
// logger. It implements the slog.Handler interface.
func (l *JSONFileLogger) WithGroup(name string) slog.Handler {
if name == "" {
return l
}
return &JSONFileLogger{file: l.file, handler: l.handler.WithGroup(name)}
}