add first metrics
This commit is contained in:
parent
59b95ea315
commit
0dbd2c72eb
34
Dockerfile
Normal file
34
Dockerfile
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
FROM golang:1.24.2 AS build
|
||||||
|
|
||||||
|
WORKDIR /go/src/middleman
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
ARG VERSION=dev
|
||||||
|
|
||||||
|
# echo -n "not tied to a commit" | sha1sum
|
||||||
|
ARG COMMIT=dbf242029aeedcfe71af2e5843474d8f8e2e9d63
|
||||||
|
|
||||||
|
RUN go build -ldflags="-s -w -B gobuildid -X main.version=$VERSION -X main.commit=$COMMIT -linkmode external -extldflags -static" -buildmode=pie -trimpath -o middleman cmd/middleman/main.go
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
|
||||||
|
COPY --from=build /etc/group /etc/group
|
||||||
|
|
||||||
|
COPY --from=build /etc/passwd /etc/passwd
|
||||||
|
|
||||||
|
WORKDIR /
|
||||||
|
|
||||||
|
COPY --from=build /go/src/middleman/middleman .
|
||||||
|
|
||||||
|
EXPOSE 2375/tcp
|
||||||
|
|
||||||
|
USER nobody:nogroup
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=5s --timeout=2s CMD ["/middleman", "healthcheck"]
|
||||||
|
|
||||||
|
ENTRYPOINT ["/middleman"]
|
||||||
|
|
||||||
|
CMD ["serve"]
|
41
README.md
41
README.md
@ -1,3 +1,42 @@
|
|||||||
# middleman
|
# middleman
|
||||||
|
|
||||||
Securely mount the Docker socket: apply fine-grained access control to Docker socket HTTP requests.
|
Securely mount the Docker socket: apply fine-grained access control to Docker socket HTTP requests.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ openssl ecparam -check -name prime256v1 -genkey -noout -out key.pem -rand /dev/urandom
|
||||||
|
$ vim traefik.cfg
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
[req]
|
||||||
|
distinguished_name = req_distinguished_name
|
||||||
|
prompt = no
|
||||||
|
default_md = sha256
|
||||||
|
|
||||||
|
[req_distinguished_name]
|
||||||
|
CN = infra.local
|
||||||
|
|
||||||
|
[database]
|
||||||
|
basicConstraints = CA:false
|
||||||
|
authorityKeyIdentifier = keyid,issuer
|
||||||
|
subjectKeyIdentifier = hash
|
||||||
|
keyUsage = digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment
|
||||||
|
extendedKeyUsage = serverAuth
|
||||||
|
subjectAltName = @alt_names
|
||||||
|
|
||||||
|
[alt_names]
|
||||||
|
DNS.1 = infra.local
|
||||||
|
DNS.2 = *.infra.local
|
||||||
|
DNS.3 = localhost
|
||||||
|
IP.1 = 127.0.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ openssl req -new -key key.pem -out csr.pem -rand /dev/urandom -config traefik.cfg
|
||||||
|
$ openssl x509 -req -days 365 -in csr.pem -signkey key.pem -out cert.pem -extensions properties -extfile traefik.cfg
|
||||||
|
```
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ docker compose --project-name infra up --detach
|
||||||
|
$ docker compose --project-name infra down --remove-orphans --volumes
|
||||||
|
```
|
@ -7,6 +7,8 @@ import (
|
|||||||
"gitea.illuad.fr/adrien/middleman"
|
"gitea.illuad.fr/adrien/middleman"
|
||||||
"gitea.illuad.fr/adrien/middleman/flag"
|
"gitea.illuad.fr/adrien/middleman/flag"
|
||||||
"gitea.illuad.fr/adrien/middleman/pkg/fastrp"
|
"gitea.illuad.fr/adrien/middleman/pkg/fastrp"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
@ -16,6 +18,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -70,7 +73,7 @@ const (
|
|||||||
// defaultShutdownTimeout is the default server shutdown timeout.
|
// defaultShutdownTimeout is the default server shutdown timeout.
|
||||||
defaultShutdownTimeout = 5 * time.Second
|
defaultShutdownTimeout = 5 * time.Second
|
||||||
// defaultHealthcheckRetry is the default number of healthcheck retry
|
// defaultHealthcheckRetry is the default number of healthcheck retry
|
||||||
defaultHealthcheckRetry uint64 = 3
|
defaultHealthcheckRetry uint = 3
|
||||||
// defaultHealthEndpoint is the default health endpoint.
|
// defaultHealthEndpoint is the default health endpoint.
|
||||||
defaultHealthEndpoint = "/healthz"
|
defaultHealthEndpoint = "/healthz"
|
||||||
// defaultHealthListenAddr is the proxy health endpoint default listen address and port.
|
// defaultHealthListenAddr is the proxy health endpoint default listen address and port.
|
||||||
@ -130,27 +133,39 @@ func Serve(group *errgroup.Group) *cli.Command {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.civo.com/learn/build-your-own-prometheus-exporter-in-go
|
||||||
func (ph *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (ph *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
middleman.RequestHandled.Inc()
|
||||||
|
timer := prometheus.NewTimer(middleman.ProcessingRequest.WithLabelValues(r.URL.Path))
|
||||||
|
defer timer.ObserveDuration()
|
||||||
|
// metric: incoming request handled
|
||||||
log.Debug().Str("remote_addr", r.RemoteAddr).Str("method", r.Method).Str("path", r.URL.Path).Msg("incoming request")
|
log.Debug().Str("remote_addr", r.RemoteAddr).Str("method", r.Method).Str("path", r.URL.Path).Msg("incoming request")
|
||||||
if mr, ok := containerMethodRegex["*"]; ok {
|
if mr, ok := containerMethodRegex["*"]; ok {
|
||||||
|
// metric: wildcard container match
|
||||||
if code := ph.checkMethodAndRegex(mr, r, ""); code != http.StatusOK {
|
if code := ph.checkMethodAndRegex(mr, r, ""); code != http.StatusOK {
|
||||||
|
// metric: request failed to match the regex for wildcard container
|
||||||
http.Error(w, http.StatusText(code), code)
|
http.Error(w, http.StatusText(code), code)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// metric: request successfully match the regex for a wildcard container
|
||||||
ph.frp.ServeHTTP(w, r)
|
ph.frp.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// metric: non wildcard container match
|
||||||
host, _, _ := net.SplitHostPort(r.RemoteAddr)
|
host, _, _ := net.SplitHostPort(r.RemoteAddr)
|
||||||
for containerName, mr := range containerMethodRegex {
|
for containerName, mr := range containerMethodRegex {
|
||||||
if ph.isContainerAuthorized(containerName, host) {
|
if ph.isContainerAuthorized(containerName, host) {
|
||||||
if code := ph.checkMethodAndRegex(mr, r, containerName); code != http.StatusOK {
|
if code := ph.checkMethodAndRegex(mr, r, containerName); code != http.StatusOK {
|
||||||
|
// metric: request failed to match the regex for a non wildcard container
|
||||||
http.Error(w, http.StatusText(code), code)
|
http.Error(w, http.StatusText(code), code)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// metric: request successfully match the regex for a non wildcard container
|
||||||
ph.frp.ServeHTTP(w, r)
|
ph.frp.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// metric: container/client? is not authorized to dial with the proxy
|
||||||
logDeniedRequest(r, http.StatusUnauthorized, "this container is not on the list of authorized ones")
|
logDeniedRequest(r, http.StatusUnauthorized, "this container is not on the list of authorized ones")
|
||||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||||
}
|
}
|
||||||
@ -158,13 +173,16 @@ func (ph *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (ph *ProxyHandler) isContainerAuthorized(containerName, host string) bool {
|
func (ph *ProxyHandler) isContainerAuthorized(containerName, host string) bool {
|
||||||
resolvedIPs, err := net.LookupIP(containerName)
|
resolvedIPs, err := net.LookupIP(containerName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// metric: failed to resolve
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for resolvedIP := range slices.Values(resolvedIPs) {
|
for resolvedIP := range slices.Values(resolvedIPs) {
|
||||||
if resolvedIP.Equal(net.ParseIP(host)) {
|
if resolvedIP.Equal(net.ParseIP(host)) { // Should I check net.ParseIP do not return a nil value?
|
||||||
|
// metric: container is authorized
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// metric: container is unauthorized
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,13 +209,16 @@ func logAuthorizedRequest(r *http.Request, containerName, message string) {
|
|||||||
func (ph *ProxyHandler) checkMethodAndRegex(mr methodRegex, r *http.Request, containerName string) int {
|
func (ph *ProxyHandler) checkMethodAndRegex(mr methodRegex, r *http.Request, containerName string) int {
|
||||||
req, ok := mr[r.Method]
|
req, ok := mr[r.Method]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
middleman.ProcessedRequest.WithLabelValues(r.Method, r.URL.Path, strconv.Itoa(http.StatusMethodNotAllowed))
|
||||||
logDeniedRequest(r, http.StatusMethodNotAllowed, "this HTTP method is not in the list of those authorized for this container")
|
logDeniedRequest(r, http.StatusMethodNotAllowed, "this HTTP method is not in the list of those authorized for this container")
|
||||||
return http.StatusMethodNotAllowed
|
return http.StatusMethodNotAllowed
|
||||||
}
|
}
|
||||||
if !req.MatchString(r.URL.Path) {
|
if !req.MatchString(r.URL.Path) {
|
||||||
|
middleman.ProcessedRequest.WithLabelValues(r.Method, r.URL.Path, strconv.Itoa(http.StatusForbidden))
|
||||||
logDeniedRequest(r, http.StatusForbidden, "this path does not match any regular expression for this HTTP method")
|
logDeniedRequest(r, http.StatusForbidden, "this path does not match any regular expression for this HTTP method")
|
||||||
return http.StatusForbidden
|
return http.StatusForbidden
|
||||||
}
|
}
|
||||||
|
middleman.ProcessedRequest.WithLabelValues(r.Method, r.URL.Path, strconv.Itoa(http.StatusOK))
|
||||||
logAuthorizedRequest(r, containerName, "incoming request matches a registered regular expression")
|
logAuthorizedRequest(r, containerName, "incoming request matches a registered regular expression")
|
||||||
return http.StatusOK
|
return http.StatusOK
|
||||||
}
|
}
|
||||||
@ -207,6 +228,20 @@ func (s serve) action(ctx context.Context, command *cli.Command) error {
|
|||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
registry := prometheus.NewRegistry()
|
||||||
|
metricsMux := http.NewServeMux()
|
||||||
|
metricsMux.Handle("/metrics", promhttp.HandlerFor(registry, promhttp.HandlerOpts{}))
|
||||||
|
registry.MustRegister(middleman.RequestHandled, middleman.ProcessingRequest, middleman.ProcessedRequest)
|
||||||
|
metricsSrv := http.Server{
|
||||||
|
Addr: ":8107",
|
||||||
|
Handler: metricsMux,
|
||||||
|
}
|
||||||
|
s.group.Go(func() error {
|
||||||
|
if err := metricsSrv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
ap, err := parseAddrPorts(command.String(listenAddrFlagName), command.String(healthListenAddrFlagName))
|
ap, err := parseAddrPorts(command.String(listenAddrFlagName), command.String(healthListenAddrFlagName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -239,7 +274,7 @@ func (s serve) action(ctx context.Context, command *cli.Command) error {
|
|||||||
if retry == 0 {
|
if retry == 0 {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Err(err).Uint64("retry_remaining", retry).Send()
|
log.Err(err).Uint("retry_remaining", retry).Send()
|
||||||
retry--
|
retry--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -283,7 +318,7 @@ func (s serve) action(ctx context.Context, command *cli.Command) error {
|
|||||||
Stringer("shutdown_timeout", command.Duration(shutdownTimeoutFlagName)).
|
Stringer("shutdown_timeout", command.Duration(shutdownTimeoutFlagName)).
|
||||||
Bool("docker_socket_healthcheck_disabled", command.Bool(noDockerSocketHealthcheckFlagName)).
|
Bool("docker_socket_healthcheck_disabled", command.Bool(noDockerSocketHealthcheckFlagName)).
|
||||||
Bool("shutdown_on_failure_disabled", command.Bool(noShutdownFlagName)).
|
Bool("shutdown_on_failure_disabled", command.Bool(noShutdownFlagName)).
|
||||||
Uint64("number_of_healthcheck_retry", command.Uint(healthcheckRetryFlagName)).
|
Uint("number_of_healthcheck_retry", command.Uint(healthcheckRetryFlagName)).
|
||||||
Str("health_endpoint", command.String(healthEndpointFlagName)).
|
Str("health_endpoint", command.String(healthEndpointFlagName)).
|
||||||
Stringer("health_endpoint_listen_addr", ap.healthAddrPort).
|
Stringer("health_endpoint_listen_addr", ap.healthAddrPort).
|
||||||
Strs("requests", command.StringSlice(addRequestsFlagName)).
|
Strs("requests", command.StringSlice(addRequestsFlagName)).
|
||||||
@ -293,6 +328,9 @@ func (s serve) action(ctx context.Context, command *cli.Command) error {
|
|||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), command.Duration(shutdownTimeoutFlagName))
|
shutdownCtx, cancel := context.WithTimeout(context.Background(), command.Duration(shutdownTimeoutFlagName))
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
if err = metricsSrv.Close(); err != nil {
|
||||||
|
log.Err(err).Send()
|
||||||
|
}
|
||||||
if err = healthSrv.Close(); err != nil {
|
if err = healthSrv.Close(); err != nil {
|
||||||
log.Err(err).Send()
|
log.Err(err).Send()
|
||||||
}
|
}
|
||||||
|
10
go.mod
10
go.mod
@ -4,12 +4,20 @@ go 1.24.2
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/rs/zerolog v1.34.0
|
github.com/rs/zerolog v1.34.0
|
||||||
github.com/urfave/cli/v3 v3.1.1
|
github.com/urfave/cli/v3 v3.3.2
|
||||||
golang.org/x/sync v0.13.0
|
golang.org/x/sync v0.13.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
|
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||||
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
|
github.com/prometheus/common v0.62.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
golang.org/x/sys v0.32.0 // indirect
|
golang.org/x/sys v0.32.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.5 // indirect
|
||||||
)
|
)
|
||||||
|
20
go.sum
20
go.sum
@ -1,3 +1,7 @@
|
|||||||
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
@ -9,7 +13,17 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP
|
|||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||||
|
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||||
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
|
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||||
|
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||||
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||||
@ -20,6 +34,10 @@ github.com/urfave/cli/v3 v3.0.0-beta1 h1:6DTaaUarcM0wX7qj5Hcvs+5Dm3dyUTBbEwIWAjc
|
|||||||
github.com/urfave/cli/v3 v3.0.0-beta1/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y=
|
github.com/urfave/cli/v3 v3.0.0-beta1/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y=
|
||||||
github.com/urfave/cli/v3 v3.1.1 h1:bNnl8pFI5dxPOjeONvFCDFoECLQsceDG4ejahs4Jtxk=
|
github.com/urfave/cli/v3 v3.1.1 h1:bNnl8pFI5dxPOjeONvFCDFoECLQsceDG4ejahs4Jtxk=
|
||||||
github.com/urfave/cli/v3 v3.1.1/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
|
github.com/urfave/cli/v3 v3.1.1/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
|
||||||
|
github.com/urfave/cli/v3 v3.2.0 h1:m8WIXY0U9LCuUl5r+0fqLWDhNYWt6qvlW+GcF4EoXf8=
|
||||||
|
github.com/urfave/cli/v3 v3.2.0/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
|
||||||
|
github.com/urfave/cli/v3 v3.3.2 h1:BYFVnhhZ8RqT38DxEYVFPPmGFTEf7tJwySTXsVRrS/o=
|
||||||
|
github.com/urfave/cli/v3 v3.3.2/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
|
||||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||||
@ -36,3 +54,5 @@ golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
|||||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||||
|
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
|
19
metric.go
Normal file
19
metric.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package middleman
|
||||||
|
|
||||||
|
import "github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
|
var (
|
||||||
|
RequestHandled = prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
|
Name: "http_requests_handled_total",
|
||||||
|
Help: "Total number of HTTP requests handled",
|
||||||
|
})
|
||||||
|
ProcessingRequest = prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
||||||
|
Name: "time_spent_processing_request_seconds",
|
||||||
|
Help: "Number of seconds spent processing a request",
|
||||||
|
}, []string{"path"})
|
||||||
|
ProcessedRequest = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||||
|
Name: "http_requests_processed_total",
|
||||||
|
Help: "Total number of HTTP requests processed",
|
||||||
|
ConstLabels: nil,
|
||||||
|
}, []string{"method", "path", "status"})
|
||||||
|
)
|
Loading…
x
Reference in New Issue
Block a user