feat: mail service
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
package internalhttp
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"galaxy/mail/internal/logging"
|
||||
"galaxy/mail/internal/telemetry"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
type edgeOutcome string
|
||||
|
||||
const (
|
||||
edgeOutcomeSuccess edgeOutcome = "success"
|
||||
edgeOutcomeRejected edgeOutcome = "rejected"
|
||||
edgeOutcomeFailed edgeOutcome = "failed"
|
||||
)
|
||||
|
||||
func instrumentRoute(route string, logger *slog.Logger, telemetryRuntime *telemetry.Runtime, next http.Handler) http.Handler {
|
||||
if logger == nil {
|
||||
logger = slog.Default()
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
startedAt := time.Now()
|
||||
recorder := &observedResponseWriter{
|
||||
ResponseWriter: writer,
|
||||
statusCode: http.StatusOK,
|
||||
}
|
||||
|
||||
next.ServeHTTP(recorder, request)
|
||||
|
||||
duration := time.Since(startedAt)
|
||||
outcome := outcomeFromStatusCode(recorder.statusCode)
|
||||
attrs := []attribute.KeyValue{
|
||||
attribute.String("route", route),
|
||||
attribute.String("method", request.Method),
|
||||
attribute.String("edge_outcome", string(outcome)),
|
||||
}
|
||||
if recorder.errorCode != "" {
|
||||
attrs = append(attrs, attribute.String("error_code", recorder.errorCode))
|
||||
}
|
||||
if telemetryRuntime != nil {
|
||||
telemetryRuntime.RecordInternalHTTPRequest(request.Context(), attrs, duration)
|
||||
}
|
||||
|
||||
logArgs := []any{
|
||||
"component", "internal_http",
|
||||
"transport", "http",
|
||||
"route", route,
|
||||
"method", request.Method,
|
||||
"status_code", recorder.statusCode,
|
||||
"duration_ms", float64(duration.Microseconds()) / 1000,
|
||||
"edge_outcome", string(outcome),
|
||||
}
|
||||
if recorder.errorCode != "" {
|
||||
logArgs = append(logArgs, "error_code", recorder.errorCode)
|
||||
}
|
||||
logArgs = append(logArgs, logging.TraceAttrsFromContext(request.Context())...)
|
||||
|
||||
switch outcome {
|
||||
case edgeOutcomeSuccess:
|
||||
logger.Info("internal request completed", logArgs...)
|
||||
case edgeOutcomeFailed:
|
||||
logger.Error("internal request failed", logArgs...)
|
||||
default:
|
||||
logger.Warn("internal request rejected", logArgs...)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type observedResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
|
||||
statusCode int
|
||||
errorCode string
|
||||
wroteHeader bool
|
||||
}
|
||||
|
||||
func (writer *observedResponseWriter) WriteHeader(statusCode int) {
|
||||
if writer.wroteHeader {
|
||||
return
|
||||
}
|
||||
|
||||
writer.statusCode = statusCode
|
||||
writer.wroteHeader = true
|
||||
writer.ResponseWriter.WriteHeader(statusCode)
|
||||
}
|
||||
|
||||
func (writer *observedResponseWriter) Write(payload []byte) (int, error) {
|
||||
if !writer.wroteHeader {
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
return writer.ResponseWriter.Write(payload)
|
||||
}
|
||||
|
||||
func (writer *observedResponseWriter) SetErrorCode(code string) {
|
||||
writer.errorCode = code
|
||||
}
|
||||
|
||||
func outcomeFromStatusCode(statusCode int) edgeOutcome {
|
||||
switch {
|
||||
case statusCode >= 500:
|
||||
return edgeOutcomeFailed
|
||||
case statusCode >= 400:
|
||||
return edgeOutcomeRejected
|
||||
default:
|
||||
return edgeOutcomeSuccess
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user