logging and client, context utils

This commit is contained in:
2024-11-16 14:02:46 -05:00
parent 46fa5ea6bf
commit 9af155dbde
15 changed files with 405 additions and 85 deletions

87
pkg/eia/eia.go Normal file
View File

@ -0,0 +1,87 @@
package eia
import (
"context"
"net/url"
"slices"
"time"
"github.com/deepmap/oapi-codegen/pkg/securityprovider"
"github.com/rs/zerolog"
eiaapi "gitea.libretechconsulting.com/50W/eia-api-go/api"
)
const (
defaultBaseURL = "https://api.eia.gov"
defaultPingTimeout = 10 * time.Second
defaultLogLevel = zerolog.DebugLevel
)
// Simple wrapper around generated EIA API client
// that embeds a client with a bearer auth interceptor
// and optional logging middleware
//
// Performs a basic availability check against the API
// when creating a new client, and exposes a Ping() method
// for health / readiness probes
type Client struct {
ctx context.Context
apiKey string
healthCheckTimeout time.Duration
*eiaapi.Client
}
type ClientOpts struct {
Context context.Context // Base context for requests
APIKey string // API Key [EIA Opendata API v2](https://www.eia.gov/opendata/index.php)
Logger *zerolog.Logger // Optional logger, if set is injected into logging middleware
LogLevel *zerolog.Level // Optional log level, default is zerolog.DebugLevel
BaseURL *url.URL // Optional, default is https://api.eia.gov
HealthCheckTimeout *time.Duration // Timeout for Ping() function, default is 10s
}
func NewClient(opts *ClientOpts) (*Client, error) {
baseURL := defaultBaseURL
if opts.BaseURL != nil {
baseURL = opts.BaseURL.String()
}
hcTimeout := defaultPingTimeout
if opts.HealthCheckTimeout != nil && *opts.HealthCheckTimeout > time.Duration(0) {
hcTimeout = *opts.HealthCheckTimeout
}
middlewares := make([]eiaapi.ClientOption, 0, 2)
// Injects Authorization: Bearer <APIKey> header into
// outbound API calls
basicAuth, err := securityprovider.NewSecurityProviderBearerToken(opts.APIKey)
if err != nil {
return nil, err
}
middlewares = append(middlewares, eiaapi.WithRequestEditorFn(basicAuth.Intercept))
// Logging middleware, if logger is given
if opts.Logger != nil {
logLevel := defaultLogLevel
if opts.LogLevel != nil {
logLevel = *opts.LogLevel
}
middlewares = append(middlewares,
eiaapi.WithRequestEditorFn(newLoggingMiddleware(opts.Logger, logLevel)))
}
client, err := eiaapi.NewClient(baseURL, slices.Clip(middlewares)...)
if err != nil {
return nil, err
}
return &Client{
apiKey: opts.APIKey,
ctx: opts.Context,
healthCheckTimeout: hcTimeout,
Client: client,
}, nil
}

20
pkg/eia/eia_health.go Normal file
View File

@ -0,0 +1,20 @@
package eia
import (
"context"
"fmt"
)
func (c *Client) Ping() error {
ctx, cncl := context.WithTimeout(c.ctx, c.healthCheckTimeout)
defer cncl()
resp, err := c.GetV2(ctx)
if err != nil {
return err
} else if resp.StatusCode != 200 {
return fmt.Errorf("non-200 response [%s] from eia api", resp.Status)
}
return nil
}

24
pkg/eia/eia_logging.go Normal file
View File

@ -0,0 +1,24 @@
package eia
import (
"context"
"net/http"
"time"
"github.com/rs/zerolog"
eiaapi "gitea.libretechconsulting.com/50W/eia-api-go/api"
)
func newLoggingMiddleware(logger *zerolog.Logger, level zerolog.Level) eiaapi.RequestEditorFn {
return func(_ context.Context, req *http.Request) error {
logger.WithLevel(level).
Str("method", req.Method).
Str("host", req.URL.Host).
Str("path", req.URL.Path).
Str("query", req.Form.Encode()).
Time("timestamp", time.Now()).
Send()
return nil
}
}