TP-44002 | Created metric middleware, publishing API metrics. Added support for Custom metrics. (#233)

This commit is contained in:
Shashank Shekhar
2023-10-17 15:17:05 +05:30
committed by GitHub
parent cf8383602b
commit 70ee05ec76
9 changed files with 161 additions and 27 deletions

View File

@@ -9,10 +9,10 @@ import (
"houston/cmd/app/handler"
"houston/internal/clients"
"houston/internal/metrics"
"houston/model/ingester"
"houston/pkg/slackbot"
"houston/service"
"net/http"
"strconv"
"strings"
"time"
)
@@ -35,6 +35,7 @@ func NewServer(gin *gin.Engine, logger *zap.Logger, db *gorm.DB, mjolnirClient *
func (s *Server) Handler(houstonGroup *gin.RouterGroup) {
s.readinessHandler(houstonGroup)
s.gin.Use(s.metricMiddleware())
s.incidentClientHandler(houstonGroup)
s.incidentClientHandlerV2(houstonGroup)
s.filtersHandlerV2(houstonGroup)
@@ -184,6 +185,27 @@ func (s *Server) Start() {
s.gin.Run(fmt.Sprintf(":%v", "8080"))
}
func (s *Server) metricMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
startTime := time.Now()
c.Next()
endTime := time.Now()
duration := endTime.Sub(startTime)
metricsPublisher := metrics.NewMetricPublisher()
apiMetrics := ingester.ApiMetric{
Url: c.Request.URL.Path,
Method: c.Request.Method,
ResponseCode: c.Writer.Status(),
StartTime: startTime.Unix(),
EndTime: endTime.Unix(),
DurationInMs: duration.Milliseconds(),
BytesSent: c.Writer.Size(),
}
metricsPublisher.PublishMetrics(ingester.MetricAttributes{ApiMetric: apiMetrics}, ingester.API_METRICS)
}
}
func (s *Server) createMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
@@ -213,17 +235,6 @@ func (s *Server) createMiddleware() gin.HandlerFunc {
c.AbortWithStatus(http.StatusOK)
return
}
startTime := time.Now()
c.Next()
endTime := float64(time.Since(startTime))
//metrics publishing
status := strconv.Itoa(c.Writer.Status())
metrics.HoustonApiRequestCounter.WithLabelValues(c.Request.URL.Path, c.Request.Method, status).Inc()
metrics.HoustonApiRequestLatencySum.WithLabelValues(c.Request.URL.Path, c.Request.Method, status).Add(endTime)
metrics.HoustonApiRequestLatencyHistogram.WithLabelValues(c.Request.URL.Path, c.Request.Method, status).Observe(endTime)
metrics.HoustonApiRequestLatencySummary.WithLabelValues(c.Request.URL.Path, c.Request.Method, status).Observe(endTime)
}
}

View File

@@ -4,6 +4,7 @@ import (
"houston/cmd/app"
"houston/config"
"houston/internal/clients"
"houston/log"
"houston/pkg/postgres"
"os"
"time"
@@ -18,6 +19,7 @@ import (
)
func main() {
log.InitLogger()
logger, _ := zap.NewProduction()
config.LoadHoustonConfig(logger)
godotenv.Load()

View File

@@ -4,7 +4,7 @@ import (
"encoding/json"
)
func JsonToStruct[JsonObject any, Struct any](obj JsonObject, result *Struct) error {
func Convert[T any, V any](obj T, result *V) error {
messageBytes, err := json.Marshal(obj)
if err != nil {
return err

2
go.mod
View File

@@ -11,6 +11,7 @@ require (
github.com/gin-contrib/zap v0.1.0
github.com/gin-gonic/gin v1.9.1
github.com/google/uuid v1.3.0
github.com/jackc/pgx/v5 v5.3.1
github.com/joho/godotenv v1.5.1
github.com/lib/pq v1.10.7
github.com/slack-go/slack v0.12.1
@@ -80,7 +81,6 @@ require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.3.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect

View File

@@ -0,0 +1,51 @@
package metrics
import (
"go.uber.org/zap"
"houston/log"
"houston/model/ingester"
"strconv"
)
type Publisher interface {
PublishMetrics(metricAttributes map[string]interface{}, metricType ingester.MetricType)
}
type PublisherImpl struct {
}
func NewMetricPublisher() *PublisherImpl {
return &PublisherImpl{}
}
func (amp *PublisherImpl) PublishMetrics(metricAttributes ingester.MetricAttributes, metricType ingester.MetricType) {
switch metricType {
case ingester.API_METRICS:
{
if err := publishApiMetric(metricAttributes.ApiMetric); err != nil {
log.Error("error while publishing api metricAttributes", zap.Error(err))
}
return
}
default:
{
return
}
}
}
func publishApiMetric(apiMetrics ingester.ApiMetric) (err error) {
defer func() {
if r := recover(); r != nil {
err = r.(error)
}
}()
status := strconv.Itoa(apiMetrics.ResponseCode)
duration := float64(apiMetrics.DurationInMs)
ApiRequestCounter.WithLabelValues(apiMetrics.Url, apiMetrics.Method, status)
ApiRequestCounter.WithLabelValues(apiMetrics.Url, apiMetrics.Method, status).Inc()
ApiRequestLatencySum.WithLabelValues(apiMetrics.Url, apiMetrics.Method, status).Add(duration)
ApiRequestLatencyHistogram.WithLabelValues(apiMetrics.Url, apiMetrics.Method, status).Observe(duration)
ApiRequestLatencySummary.WithLabelValues(apiMetrics.Url, apiMetrics.Method, status).Observe(duration)
return
}

View File

@@ -5,7 +5,7 @@ import (
"github.com/prometheus/client_golang/prometheus/promauto"
)
var HoustonApiRequestCounter = promauto.NewCounterVec(
var ApiRequestCounter = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "houston_api_request_total",
Help: "api request counter",
@@ -13,7 +13,7 @@ var HoustonApiRequestCounter = promauto.NewCounterVec(
[]string{"url", "method", "response_code"},
)
var HoustonApiRequestLatencySum = promauto.NewCounterVec(
var ApiRequestLatencySum = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "houston_api_request_latency_sum",
Help: "api request latency sum",
@@ -21,7 +21,7 @@ var HoustonApiRequestLatencySum = promauto.NewCounterVec(
[]string{"url", "method", "response_code"},
)
var HoustonApiRequestLatencyHistogram = promauto.NewHistogramVec(
var ApiRequestLatencyHistogram = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "houston_api_request_latency_histogram",
Help: "api latency histogram",
@@ -29,7 +29,7 @@ var HoustonApiRequestLatencyHistogram = promauto.NewHistogramVec(
[]string{"url", "method", "response_code"},
)
var HoustonApiRequestLatencySummary = promauto.NewSummaryVec(
var ApiRequestLatencySummary = promauto.NewSummaryVec(
prometheus.SummaryOpts{
Name: "houston_api_request_latency_summary",
Help: "api latency summary",

46
log/log.go Normal file
View File

@@ -0,0 +1,46 @@
package log
import (
"go.uber.org/zap"
)
type Logger struct {
log *zap.Logger
}
var Log *Logger
func InitLogger() {
log, err := zap.NewProduction()
if err != nil {
panic(err)
}
Log = &Logger{
log: log,
}
}
func GetLogger() *zap.Logger {
return Log.log
}
func Error(message string, fields ...zap.Field) {
GetLogger().Error(message, fields...)
}
func Warn(message string, fields ...zap.Field) {
GetLogger().Warn(message, fields...)
}
func Info(message string, fields ...zap.Field) {
GetLogger().Info(message, fields...)
}
func Fatal(message string, fields ...zap.Field) {
GetLogger().Fatal(message, fields...)
}
func Panic(message string, fields ...zap.Field) {
GetLogger().Panic(message, fields...)
}

View File

@@ -0,0 +1,24 @@
package ingester
type MetricType string
const (
API_METRICS MetricType = "API_METRICS"
)
type ApiMetric struct {
Url string `json:"url,omitempty"`
Method string `json:"method,omitempty"`
ResponseCode int `json:"response_code,omitempty"`
BytesSent int `json:"bytes_sent,omitempty"`
BytesReceived int64 `json:"bytes_received,omitempty"`
StartTime int64 `json:"start_time,omitempty"`
EndTime int64 `json:"end_time,omitempty"`
DurationInMs int64 `json:"duration_in_ms,omitempty"`
ErrorType string `json:"error_type,omitempty"`
ErrorMessage string `json:"error_message,omitempty"`
}
type MetricAttributes struct {
ApiMetric
}

View File

@@ -6,16 +6,16 @@ import (
"github.com/gin-gonic/gin"
)
type readinessService struct {
gin *gin.Engine
type ReadinessService struct {
gin *gin.Engine
}
func NewReadinessService(gin *gin.Engine) *readinessService {
return &readinessService{
gin: gin,
}
func NewReadinessService(gin *gin.Engine) *ReadinessService {
return &ReadinessService{
gin: gin,
}
}
func (i *readinessService) Ping(c *gin.Context) {
c.JSON(http.StatusOK, "pong")
}
func (i *ReadinessService) Ping(c *gin.Context) {
c.JSON(http.StatusOK, "pong")
}