components/otelopscol/receiver/varnishreceiver/client.go (73 lines of code) (raw):
// Copyright 2022 Google LLC
//
// 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 varnishreceiver
import (
"encoding/json"
"os/exec"
"path/filepath"
"go.opentelemetry.io/collector/component"
"go.uber.org/zap"
)
// executer executes commands.
type executer interface {
Execute(command string, args []string) ([]byte, error)
}
// varnishExecuter executes varnish commands.
type varnishExecuter struct{}
// newExecuter creates a new executer.
func newExecuter() executer {
return &varnishExecuter{}
}
// Execute executes commands with args flag.
func (e *varnishExecuter) Execute(command string, args []string) ([]byte, error) {
return exec.Command(command, args...).Output()
}
// client is an interface to get stats and build the exec command using an executer.
type client interface {
GetStats() (*Stats, error)
}
var _ client = (*varnishClient)(nil)
type varnishClient struct {
exec executer
cfg *Config
logger *zap.Logger
}
// newVarnishClient creates a client.
func newVarnishClient(cfg *Config, _ component.Host, settings component.TelemetrySettings) client {
return &varnishClient{
cfg: cfg,
logger: settings.Logger,
exec: newExecuter(),
}
}
const (
varnishStat = "varnishstat"
counters = "counters"
)
// BuildCommand builds the exec command statement.
func (v *varnishClient) BuildCommand() (string, []string) {
argList := []string{"-j"}
argList = append(argList, "-n", v.cfg.CacheDir)
command := varnishStat
if v.cfg.ExecDir != "" {
command = filepath.Join(v.cfg.ExecDir, command)
}
return command, argList
}
// GetStats executes and parses the varnish stats.
func (v *varnishClient) GetStats() (*Stats, error) {
command, argList := v.BuildCommand()
output, err := v.exec.Execute(command, argList)
if err != nil {
return nil, err
}
return parseStats(output)
}
// parseStats parses varnishStats json response into a Stats struct.
func parseStats(rawStats []byte) (*Stats, error) {
raw := make(map[string]interface{})
if err := json.Unmarshal(rawStats, &raw); err != nil {
return nil, err
}
// Varnish 6.5+ nests metrics inside a "counters" field.
// https://varnish-cache.org/docs/6.5/whats-new/upgrading-6.5.html#varnishstat
if _, ok := raw[counters]; ok {
var jsonParsed FullStats
if err := json.Unmarshal(rawStats, &jsonParsed); err != nil {
return nil, err
}
return &jsonParsed.Counters, nil
}
var jsonParsed Stats
if err := json.Unmarshal(rawStats, &jsonParsed); err != nil {
return nil, err
}
return &jsonParsed, nil
}