102 lines
2.2 KiB
Go
102 lines
2.2 KiB
Go
package lokiunifi
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/tls"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
lokiPushPath = "/loki/api/v1/push"
|
|
)
|
|
|
|
var errStatusCode = fmt.Errorf("unexpected HTTP status code")
|
|
|
|
// Client holds the http client for contacting Loki.
|
|
type Client struct {
|
|
*Config
|
|
*http.Client
|
|
}
|
|
|
|
func (l *Loki) httpClient() *Client {
|
|
return &Client{
|
|
Config: l.Config,
|
|
Client: &http.Client{
|
|
Timeout: l.Timeout.Duration,
|
|
Transport: &http.Transport{
|
|
TLSClientConfig: &tls.Config{
|
|
InsecureSkipVerify: !l.VerifySSL, // nolint: gosec
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// Post marshals and posts a batch of log messages.
|
|
func (c *Client) Post(logs interface{}) error {
|
|
msg, err := json.Marshal(logs)
|
|
if err != nil {
|
|
return fmt.Errorf("json marshal: %w", err)
|
|
}
|
|
|
|
u := strings.TrimSuffix(c.URL, lokiPushPath) + lokiPushPath
|
|
|
|
req, err := c.NewRequest(u, "POST", "application/json", msg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if code, body, err := c.Do(req); err != nil {
|
|
return err
|
|
} else if code != http.StatusNoContent {
|
|
m := fmt.Sprintf("%s (%d/%s) %s, msg: %s", u, code, http.StatusText(code),
|
|
strings.TrimSpace(strings.ReplaceAll(string(body), "\n", " ")), msg)
|
|
|
|
return fmt.Errorf("%s: %w", m, errStatusCode)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// NewRequest creates the http request based on input data.
|
|
func (c *Client) NewRequest(url, method, cType string, msg []byte) (*http.Request, error) {
|
|
req, err := http.NewRequest(method, url, bytes.NewBuffer(msg)) //nolint:noctx
|
|
if err != nil {
|
|
return nil, fmt.Errorf("creating request: %w", err)
|
|
}
|
|
|
|
if cType != "" {
|
|
req.Header.Set("Content-Type", cType)
|
|
}
|
|
|
|
if c.Username != "" || c.Password != "" {
|
|
req.SetBasicAuth(c.Username, c.Password)
|
|
}
|
|
|
|
if c.TenantID != "" {
|
|
req.Header.Set("X-Scope-OrgID", c.TenantID)
|
|
}
|
|
|
|
return req, nil
|
|
}
|
|
|
|
// Do makes an http request and returns the status code, body and/or an error.
|
|
func (c *Client) Do(req *http.Request) (int, []byte, error) {
|
|
resp, err := c.Client.Do(req)
|
|
if err != nil {
|
|
return 0, nil, fmt.Errorf("making request: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return resp.StatusCode, body, fmt.Errorf("reading body: %w", err)
|
|
}
|
|
|
|
return resp.StatusCode, body, nil
|
|
}
|