Documentation
¶
Overview ¶
Package gslog contains a GCP logging implementation of slog.Handler.
Index ¶
- Constants
- func ExtractLabels(ctx context.Context) map[string]string
- func WithDefaultLogLeveler(defaultLogLevel slog.Leveler) options.OptionProcessor
- func WithLabels(ctx context.Context, labelPairs ...LabelPair) context.Context
- func WithLogLevelFromEnvVar(key string) options.OptionProcessor
- func WithLogLeveler(logLevel slog.Leveler) options.OptionProcessor
- func WithReplaceAttr(replaceAttr AttrMapper) options.OptionProcessor
- func WithSourceAdded() options.OptionProcessor
- type AttrMapper
- type GcpHandler
- func (h *GcpHandler) Enabled(_ context.Context, level slog.Level) bool
- func (h *GcpHandler) Flush() error
- func (h *GcpHandler) Handle(ctx context.Context, record slog.Record) error
- func (h *GcpHandler) WithAttrs(attrs []slog.Attr) slog.Handler
- func (h *GcpHandler) WithGroup(name string) slog.Handler
- func (h *GcpHandler) WithLeveler(leveler slog.Leveler) *GcpHandler
- type LabelPair
- type Log
- type LogSync
- type Logger
- type LoggerFunc
- type Options
Examples ¶
- GcpHandler.Handle (PayloadMapping)
- GcpHandler.Handle (WithLabels)
- NewGcpHandler
- NewGcpHandler (WithDefaultLogLeveler)
- NewGcpHandler (WithK8sPodinfo)
- NewGcpHandler (WithLogLevelFromEnvVar)
- NewGcpHandler (WithLogLeveler)
- NewGcpHandler (WithOpentelemetryBaggage)
- NewGcpHandler (WithOpentelemetryTrace)
- NewGcpHandler (WithReplaceAttr)
- NewGcpHandler (WithSourceAdded)
Constants ¶
const ( // LevelNotice means normal but significant events, such as start up, // shut down, or configuration. LevelNotice = slog.Level(2) // LevelCritical means events that cause more severe problems or brief // outages. LevelCritical = slog.Level(12) // LevelAlert means a person must take an action immediately. LevelAlert = slog.Level(16) // LevelEmergency means one or more systems are unusable. LevelEmergency = slog.Level(20) )
const ( // MessageKey is the key used for the message of the log call, per Google // Cloud Logging. The associated value is a string. MessageKey = "message" )
Variables ¶
This section is empty.
Functions ¶
func ExtractLabels ¶
ExtractLabels extracts labels from the ctx. These labels were associated with the context using WithLabels.
func WithDefaultLogLeveler ¶ added in v0.16.0
func WithDefaultLogLeveler(defaultLogLevel slog.Leveler) options.OptionProcessor
WithDefaultLogLeveler returns an option that specifies the default slog.Leveler for logging.
func WithLabels ¶
WithLabels returns a new Context with labels to be used in the GCP log entries produced using that context.
func WithLogLevelFromEnvVar ¶
func WithLogLevelFromEnvVar(key string) options.OptionProcessor
WithLogLevelFromEnvVar returns an option that specifies the log level for logging comes from tne environmental variable specified by the key.
func WithLogLeveler ¶ added in v0.16.0
func WithLogLeveler(logLevel slog.Leveler) options.OptionProcessor
WithLogLeveler returns an option that specifies the slog.Leveler for logging. Explicitly setting the log level here takes precedence over the other options.
func WithReplaceAttr ¶
func WithReplaceAttr(replaceAttr AttrMapper) options.OptionProcessor
WithReplaceAttr returns an option that specifies an attribute mapper used to rewrite each non-group attribute before it is logged.
func WithSourceAdded ¶
func WithSourceAdded() options.OptionProcessor
WithSourceAdded returns an option that causes the handler to compute the source code position of the log statement and add a slog.SourceKey attribute to the output.
Types ¶
type AttrMapper ¶
AttrMapper is called to rewrite each non-group attribute before it is logged. The attribute's value has been resolved (see [Value.Resolve]). If replaceAttr returns a zero Attr, the attribute is discarded.
The built-in attribute with key "message" is passed to this function.
The first argument is a list of currently open groups that contain the Attr. It must not be retained or modified. replaceAttr is never called for Group attributes, only their contents. For example, the attribute list
Int("a", 1), Group("g", Int("b", 2)), Int("c", 3)
results in consecutive calls to replaceAttr with the following arguments:
nil, Int("a", 1) []string{"g"}, Int("b", 2) nil, Int("c", 3)
AttrMapper can be used to change the default keys of the built-in attributes, convert types (for example, to replace a `time.Time` with the integer seconds since the Unix epoch), sanitize personal information, or remove attributes from the output.
type GcpHandler ¶
type GcpHandler struct {
// contains filtered or unexported fields
}
GcpHandler is a Google Cloud Logging backed slog handler.
func NewGcpHandler ¶
func NewGcpHandler(logger Logger, opts ...options.OptionProcessor) *GcpHandler
NewGcpHandler creates a Google Cloud Logging backed log.Logger.
Example ¶
A gslog.GcpHandler is created with a GCP logging.Logger. The handler will map slog.Record records to logging.Entry entries, subsequently passing the resulting entries to its configured logging.Logger instance's Log() method.
package main import ( "context" "log/slog" "cloud.google.com/go/logging" "m4o.io/gslog" ) func main() { ctx := context.Background() client, err := logging.NewClient(ctx, "my-project") if err != nil { // TODO: Handle error. } lg := client.Logger("my-log") lg.Flush() h := gslog.NewGcpHandler(lg) l := slog.New(h) l.Info("How now brown cow?") }
Output:
Example (WithDefaultLogLeveler) ¶
A default log level configured via gslog.WithDefaultLogLeveler().
package main import ( "encoding/json" "fmt" "log/slog" "cloud.google.com/go/logging" "google.golang.org/protobuf/encoding/protojson" spb "google.golang.org/protobuf/types/known/structpb" "m4o.io/gslog" ) // PrintJsonPayload is a gslog.Logger stub that prints the logging.Entry // Payload field as a JSON string. func PrintJsonPayload(e logging.Entry) { b, _ := protojson.Marshal(e.Payload.(*spb.Struct)) var j map[string]interface{} _ = json.Unmarshal(b, &j) b, _ = json.Marshal(j) fmt.Println(string(b)) } func main() { const envVar = "FOO_LOG_LEVEL" h := gslog.NewGcpHandler( gslog.LoggerFunc(PrintJsonPayload), gslog.WithLogLevelFromEnvVar(envVar), gslog.WithDefaultLogLeveler(slog.LevelError), ) l := slog.New(h) l.Info("How now brown cow?") l.Error("The rain in Spain lies mainly on the plane.") }
Output: {"message":"The rain in Spain lies mainly on the plane."}
Example (WithK8sPodinfo) ¶
When configured via k8s.WithPodinfoLabels(), gslog.GcpHandler will include labels from the configured Kubernetes Downward API podinfo labels file to the logging.Entry's Labels field.
The labels are prefixed with "k8s-pod/" to adhere to the Google Cloud Logging conventions for Kubernetes Pod labels.
package main import ( "context" "fmt" "log/slog" "sort" "strings" "cloud.google.com/go/logging" "m4o.io/gslog" "m4o.io/gslog/k8s" ) // PrintLabels is a gslog.Logger stub that prints the logging.Entry's // Labels field. func PrintLabels(e logging.Entry) { keys := make([]string, 0) for k := range e.Labels { keys = append(keys, k) } sort.Strings(keys) var sb strings.Builder for _, k := range keys { if sb.Len() > 0 { sb.WriteString(", ") } sb.WriteString(k + "=" + e.Labels[k]) } fmt.Println(sb.String()) } func main() { h := gslog.NewGcpHandler(gslog.LoggerFunc(PrintLabels), k8s.WithPodinfoLabels("k8s/testdata/etc/podinfo")) l := slog.New(h) ctx := context.Background() ctx = gslog.WithLabels(ctx, gslog.Label("a", "one"), gslog.Label("b", "two")) l.Log(ctx, gslog.LevelCritical, "Danger, Will Robinson!") }
Output: a=one, b=two, k8s-pod/app=hello-world, k8s-pod/environment=stg, k8s-pod/tier=backend, k8s-pod/track=stable
Example (WithLogLevelFromEnvVar) ¶
When configured via gslog.WithLogLevelFromEnvVar(), gslog.GcpHandler obtains its log level from tne environmental variable specified by the key.
package main import ( "encoding/json" "fmt" "log/slog" "os" "cloud.google.com/go/logging" "google.golang.org/protobuf/encoding/protojson" spb "google.golang.org/protobuf/types/known/structpb" "m4o.io/gslog" ) // PrintJsonPayload is a gslog.Logger stub that prints the logging.Entry // Payload field as a JSON string. func PrintJsonPayload(e logging.Entry) { b, _ := protojson.Marshal(e.Payload.(*spb.Struct)) var j map[string]interface{} _ = json.Unmarshal(b, &j) b, _ = json.Marshal(j) fmt.Println(string(b)) } func main() { const envVar = "FOO_LOG_LEVEL" _ = os.Setenv(envVar, "ERROR") defer func() { _ = os.Unsetenv(envVar) }() h := gslog.NewGcpHandler(gslog.LoggerFunc(PrintJsonPayload), gslog.WithLogLevelFromEnvVar(envVar)) l := slog.New(h) l.Info("How now brown cow?") l.Error("The rain in Spain lies mainly on the plane.") }
Output: {"message":"The rain in Spain lies mainly on the plane."}
Example (WithLogLeveler) ¶
When configured via gslog.WithLogLeveler(), gslog.GcpHandler use the slog.Leveler for logging level enabled checks.
package main import ( "encoding/json" "fmt" "log/slog" "cloud.google.com/go/logging" "google.golang.org/protobuf/encoding/protojson" spb "google.golang.org/protobuf/types/known/structpb" "m4o.io/gslog" ) // PrintJsonPayload is a gslog.Logger stub that prints the logging.Entry // Payload field as a JSON string. func PrintJsonPayload(e logging.Entry) { b, _ := protojson.Marshal(e.Payload.(*spb.Struct)) var j map[string]interface{} _ = json.Unmarshal(b, &j) b, _ = json.Marshal(j) fmt.Println(string(b)) } func main() { h := gslog.NewGcpHandler(gslog.LoggerFunc(PrintJsonPayload), gslog.WithLogLeveler(slog.LevelError)) l := slog.New(h) l.Info("How now brown cow?") l.Error("The rain in Spain lies mainly on the plane.") }
Output: {"message":"The rain in Spain lies mainly on the plane."}
Example (WithOpentelemetryBaggage) ¶
When configured via otel.WithOtelBaggage(), gslog.GcpHandler will include any baggage.Baggage attached to the context as attributes.
The baggage keys are prefixed with "otel-baggage/" to mitigate collision with other log attributes and have precedence over any collisions with preexisting attributes.
package main import ( "context" "encoding/json" "fmt" "log/slog" "cloud.google.com/go/logging" "go.opentelemetry.io/otel/baggage" "google.golang.org/protobuf/encoding/protojson" spb "google.golang.org/protobuf/types/known/structpb" "m4o.io/gslog" "m4o.io/gslog/otel" ) // PrintJsonPayload is a gslog.Logger stub that prints the logging.Entry // Payload field as a JSON string. func PrintJsonPayload(e logging.Entry) { b, _ := protojson.Marshal(e.Payload.(*spb.Struct)) var j map[string]interface{} _ = json.Unmarshal(b, &j) b, _ = json.Marshal(j) fmt.Println(string(b)) } func main() { h := gslog.NewGcpHandler(gslog.LoggerFunc(PrintJsonPayload), otel.WithOtelBaggage()) l := slog.New(h) ctx := context.Background() ctx = baggage.ContextWithBaggage(ctx, otel.MustParse("a=one,b=two;p1;p2=val2")) l.Log(ctx, slog.LevelInfo, "How now brown cow?") }
Output: {"message":"How now brown cow?","otel-baggage/a":"one","otel-baggage/b":{"properties":{"p1":null,"p2":"val2"},"value":"two"}}
Example (WithOpentelemetryTrace) ¶
When configured via otel.WithOtelTracing(), gslog.GcpHandler will include any OpenTelemetry trace.SpanContext information associated with the context in the logging.Entry's tracing fields.
package main import ( "context" "fmt" "log/slog" "strings" "cloud.google.com/go/logging" "go.opentelemetry.io/otel/trace" "m4o.io/gslog" "m4o.io/gslog/otel" ) // PrintTracing is a gslog.Logger stub that prints the logging.Entry's // tracing fields. func PrintTracing(e logging.Entry) { var sb strings.Builder sb.WriteString("trace: ") sb.WriteString(e.Trace) sb.WriteString(" span: ") sb.WriteString(e.SpanID) sb.WriteString(" flags: ") if e.TraceSampled { sb.WriteString("01") } else { sb.WriteString("00") } fmt.Println(sb.String()) } func main() { h := gslog.NewGcpHandler(gslog.LoggerFunc(PrintTracing), otel.WithOtelTracing("my-project")) l := slog.New(h) traceId, _ := trace.TraceIDFromHex("52fc1643a9381fc674742bb0067101e7") spanId, _ := trace.SpanIDFromHex("d3e9e8c51cb190df") ctx := context.Background() ctx = trace.ContextWithRemoteSpanContext(ctx, trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceId, SpanID: spanId, TraceFlags: trace.FlagsSampled, })) l.Log(ctx, slog.LevelInfo, "How now brown cow?") }
Output: trace: projects/my-project/traces/52fc1643a9381fc674742bb0067101e7 span: d3e9e8c51cb190df flags: 01
Example (WithReplaceAttr) ¶
When configured via gslog.WithReplaceAttr(), gslog.GcpHandler will apply the supplied gslog.AttrMapper to all non-group attributes before they are logged.
package main import ( "encoding/json" "fmt" "log/slog" "strconv" "cloud.google.com/go/logging" "google.golang.org/protobuf/encoding/protojson" spb "google.golang.org/protobuf/types/known/structpb" "m4o.io/gslog" ) var ( pw = Password("pass-12334") pwObfuscated = slog.StringValue("<secret>") ) type Manager struct{} // Password is a specialised type whose fmt.Stringer, json.Marshaler, and // slog.LogValuer implementations return an obfuscated value. type Password string func (p Password) String() string { return "<secret>" } func (p Password) MarshalJSON() ([]byte, error) { return []byte(strconv.Quote("<secret>")), nil } func (p Password) LogValue() slog.Value { return pwObfuscated } type User struct { ID string `json:"id"` FirstName string `json:"first_name"` LastName string `json:"last_name"` Email string `json:"email"` Password Password `json:"password"` Age uint8 `json:"age"` Height float32 `json:"height"` Engineer bool `json:"engineer"` Manager *Manager `json:"manager"` } // PrintJsonPayload is a gslog.Logger stub that prints the logging.Entry // Payload field as a JSON string. func PrintJsonPayload(e logging.Entry) { b, _ := protojson.Marshal(e.Payload.(*spb.Struct)) var j map[string]interface{} _ = json.Unmarshal(b, &j) b, _ = json.Marshal(j) fmt.Println(string(b)) } // RemovePassword is a gslog.AttrMapper that elides password attributes. func RemovePassword(_ []string, a slog.Attr) slog.Attr { if a.Key == "password" { return slog.Attr{} } return a } func main() { h := gslog.NewGcpHandler(gslog.LoggerFunc(PrintJsonPayload), gslog.WithReplaceAttr(RemovePassword)) l := slog.New(h) l = l.WithGroup("pub") l = l.With(slog.String("username", "user-12234"), slog.String("password", string(pw))) l.Info("How now brown cow?") }
Output: {"message":"How now brown cow?","pub":{"username":"user-12234"}}
Example (WithSourceAdded) ¶
When configured via gslog.WithSourceAdded(), gslog.GcpHandler will include the computationally expensive SourceLocation field in the logging.Entry.
h := gslog.NewGcpHandler(gslog.LoggerFunc(PrintSourceLocation), gslog.WithSourceAdded()) l := slog.New(h) l.Log(ctx, slog.LevelInfo, "How now brown cow?")
Output: {"file":"gslog/example_test.go","function":"m4o.io/gslog_test.ExampleNewGcpHandler_withSourceAdded","line":"259"}
func (*GcpHandler) Enabled ¶
Enabled reports whether the handler handles records at the given level. The handler ignores records whose level is lower.
func (*GcpHandler) Flush ¶ added in v0.17.0
func (h *GcpHandler) Flush() error
Flush blocks until all currently buffered log entries are sent.
If any errors occurred since the last call to Flush from any Logger, or the creation of the client if this is the first call, then Flush returns a non-nil error with summary information about the errors. This information is unlikely to be actionable. For more accurate error reporting, set Client.OnError.
func (*GcpHandler) Handle ¶
Handle will handle a slog.Record, as described in the interface's documentation. It will translate the slog.Record into a logging.Entry that's filled with a *spb.Value as an Entry Payload.
Example (PayloadMapping) ¶
The gslog.GcpHandler maps the slog.Record and the handler's nested group attributes into a JSON object, with the logged message keyed at the root with the key "message".
package main import ( "encoding/json" "fmt" "log/slog" "strconv" "cloud.google.com/go/logging" "google.golang.org/protobuf/encoding/protojson" spb "google.golang.org/protobuf/types/known/structpb" "m4o.io/gslog" ) var ( pw = Password("pass-12334") pwObfuscated = slog.StringValue("<secret>") u = &User{ ID: "user-12234", FirstName: "Jan", LastName: "Doe", Email: "jan@example.com", Password: pw, Age: 32, Height: 5.91, Engineer: true, } ) type Manager struct{} // Password is a specialised type whose fmt.Stringer, json.Marshaler, and // slog.LogValuer implementations return an obfuscated value. type Password string func (p Password) String() string { return "<secret>" } func (p Password) MarshalJSON() ([]byte, error) { return []byte(strconv.Quote("<secret>")), nil } func (p Password) LogValue() slog.Value { return pwObfuscated } type User struct { ID string `json:"id"` FirstName string `json:"first_name"` LastName string `json:"last_name"` Email string `json:"email"` Password Password `json:"password"` Age uint8 `json:"age"` Height float32 `json:"height"` Engineer bool `json:"engineer"` Manager *Manager `json:"manager"` } // PrintJsonPayload is a gslog.Logger stub that prints the logging.Entry // Payload field as a JSON string. func PrintJsonPayload(e logging.Entry) { b, _ := protojson.Marshal(e.Payload.(*spb.Struct)) var j map[string]interface{} _ = json.Unmarshal(b, &j) b, _ = json.Marshal(j) fmt.Println(string(b)) } func main() { h := gslog.NewGcpHandler(gslog.LoggerFunc(PrintJsonPayload)) l := slog.New(h) l = l.WithGroup("pub") l = l.With(slog.Any("user", u)) l.Info("How now brown cow?") }
Output: {"message":"How now brown cow?","pub":{"user":{"age":32,"email":"jan@example.com","engineer":true,"first_name":"Jan","height":5.91,"id":"user-12234","last_name":"Doe","manager":null,"password":"\u003csecret\u003e"}}}
Example (WithLabels) ¶
The gslog.GcpHandler will add any labels found in the context to the logging.Entry's Labels field.
package main import ( "context" "fmt" "log/slog" "sort" "strings" "cloud.google.com/go/logging" "m4o.io/gslog" ) // PrintLabels is a gslog.Logger stub that prints the logging.Entry's // Labels field. func PrintLabels(e logging.Entry) { keys := make([]string, 0) for k := range e.Labels { keys = append(keys, k) } sort.Strings(keys) var sb strings.Builder for _, k := range keys { if sb.Len() > 0 { sb.WriteString(", ") } sb.WriteString(k + "=" + e.Labels[k]) } fmt.Println(sb.String()) } func main() { h := gslog.NewGcpHandler(gslog.LoggerFunc(PrintLabels)) l := slog.New(h) ctx := context.Background() ctx = gslog.WithLabels(ctx, gslog.Label("a", "one"), gslog.Label("b", "two")) l.Log(ctx, slog.LevelInfo, "How now brown cow?") }
Output: a=one, b=two
func (*GcpHandler) WithAttrs ¶
func (h *GcpHandler) WithAttrs(attrs []slog.Attr) slog.Handler
WithAttrs returns a copy of the handler whose attributes consists of h's attributes followed by attrs.
func (*GcpHandler) WithGroup ¶
func (h *GcpHandler) WithGroup(name string) slog.Handler
WithGroup returns a copy of the handler with the given group appended to the receiver's existing groups.
func (*GcpHandler) WithLeveler ¶ added in v0.2.0
func (h *GcpHandler) WithLeveler(leveler slog.Leveler) *GcpHandler
WithLeveler returns a copy of the handler, provisioned with the supplied leveler.
type LabelPair ¶
type LabelPair struct {
// contains filtered or unexported fields
}
LabelPair represents a key-value string pair.
type Log ¶ added in v0.5.0
type Log interface { // Log buffers the Entry for output to the logging service. It never blocks. Log(e logging.Entry) }
Log wraps the asynchronous buffered logging of records to Google Cloud Logging.
type LogSync ¶ added in v0.5.0
type LogSync interface { // LogSync logs the Entry synchronously without any buffering. Because LogSync is slow // and will block, it is intended primarily for debugging or critical errors. // Prefer Log for most uses. LogSync(ctx context.Context, e logging.Entry) error }
LogSync wraps the synchronous logging of records to Google Cloud Logging.
type Logger ¶ added in v0.5.0
type Logger interface { Log LogSync // Flush blocks until all currently buffered log entries are sent. // // If any errors occurred since the last call to Flush from any Logger, or the // creation of the client if this is the first call, then Flush returns a non-nil // error with summary information about the errors. This information is unlikely to // be actionable. For more accurate error reporting, set Client.OnError. Flush() error }
Logger is wraps the set of methods that are used when interacting with a logging.Logger. This interface facilitates stubbing out calls to the Logger for the purposes of testing and benchmarking.
type LoggerFunc ¶ added in v0.11.0
The LoggerFunc type is an adapter to allow the use of ordinary functions as a Logger. If fn is a function with the appropriate signature, LoggerFunc(fn) is a Logger that calls fn.
func (LoggerFunc) Flush ¶ added in v0.17.0
func (fn LoggerFunc) Flush() error
Flush implements Logger.Flush.
func (LoggerFunc) Log ¶ added in v0.11.0
func (fn LoggerFunc) Log(e logging.Entry)
Log implements Log.Log.
Source Files
¶
Directories
¶
Path | Synopsis |
---|---|
internal
|
|
attr
Package attr contains code that maps slog.Attr attributes to their corresponding structpb.Value values.
|
Package attr contains code that maps slog.Attr attributes to their corresponding structpb.Value values. |
level
Package level contains code that maps slog.Level levels to logging.Severity.
|
Package level contains code that maps slog.Level levels to logging.Severity. |
options
Package options holds the options handling code.
|
Package options holds the options handling code. |
Package k8s contains options for including labels from the Kubernetes Downward API podinfo labels file in logging records.
|
Package k8s contains options for including labels from the Kubernetes Downward API podinfo labels file in logging records. |
Package otel contains options for including OpenTelemetry baggage and tracing in logging records.
|
Package otel contains options for including OpenTelemetry baggage and tracing in logging records. |