Files
zeekay b9f9cdf918 feat: bump o11y fork to latest SigNoz — full re-sync, builds green (§4c retained)
Re-sync the entire SigNoz-derived Go tree to a clean fork of latest SigNoz
main (upstream 3e6339019), eliminating the version-skew that broke
`go build ./...` (mixed-era packages, ~14 imported-but-absent packages,
partial signoz->o11y / clickhouse->datastore rebrands, self-import cycle,
duplicate package decls).

- pkg/ + cmd/ wholesale replaced with latest SigNoz; imports rewritten
  github.com/SigNoz/signoz/ -> github.com/hanzoai/o11y/.
- Adopt SigNoz's CURRENT structure (resourcefilter->telemetryresourcefilter,
  integrationtypes->cloudintegrationtypes, roletypes/grammar/times folded in
  upstream); old paths dropped.
- Fork deps repathed to hanzoai + bumped: signoz-otel-collector v0.144.6,
  clickhouse-go-mock v0.14.1, govaluate v0.1.0; expr-lang/expr => hanzoai/expr
  v1.17.8 (replace). go.mod reconciled from latest SigNoz; go.sum re-tidied.
- ee/ + cmd/enterprise stripped — o11y stays MIT-only; community edition
  imports no ee packages.
- Preserved o11y additions: module path github.com/hanzoai/o11y, mount.go
  (cloud.Register/zip mount adapter, HIP-0106), NOTICE + LICENSE (SigNoz §4c
  attribution retained; upstream pkg files are headerless per SigNoz convention).
- Reverted o11y cosmetic renames (o11y*/datastore*) and experimental originals
  (authz/iamauthz, billing, zapreceiver, zapmetricreceiver) to SigNoz canonical
  to kill the skew; snapshots retained in git history for re-layering.

go build ./... = exit 0. go vet clean except benign "unreachable code" in
generated ANTLR parser files (inherited from upstream).
2026-07-02 14:20:32 -07:00

137 lines
4.2 KiB
Go

package render
import (
"math"
"net/http"
"strconv"
"github.com/hanzoai/o11y/pkg/errors"
jsoniter "github.com/json-iterator/go"
"github.com/tidwall/gjson"
)
const (
// Non-standard status code (originally introduced by nginx) for the case when a client closes
// the connection while the server is still processing the request.
statusClientClosedConnection = 499
)
var json = jsoniter.ConfigCompatibleWithStandardLibrary
type SuccessResponse struct {
Status string `json:"status" required:"true"`
Data interface{} `json:"data,omitempty" required:"true"`
}
type ErrorResponse struct {
Status string `json:"status" required:"true"`
Error *errors.JSON `json:"error" required:"true"`
}
func Success(rw http.ResponseWriter, httpCode int, data interface{}) {
body, err := json.Marshal(&SuccessResponse{Status: StatusSuccess.s, Data: data})
if err != nil {
Error(rw, err)
return
}
if httpCode == 0 {
httpCode = http.StatusOK
}
rw.Header().Set("Content-Type", "application/json")
rw.WriteHeader(httpCode)
_, _ = rw.Write(body)
}
func ErrorCodeFromBody(body []byte) string {
code := gjson.GetBytes(body, "error.code").String()
// This should never return empty since we only call this function on responses that were generated by us.
// If it does return empty, the codebase has failed to use render package for error responses somewhere, and we should fix that instead of trying to handle it here.
if code == "" {
return errors.CodeUnset.String()
}
return code
}
func ErrorTypeFromStatusCode(statusCode int) string {
// We are losing the exact type information here, but we can at least capture the error code and message for better observability.
// To get the exact type, we would need some changes in the render package to include the error type in the response, which we can consider in the future if there is a need for it.
switch statusCode {
case http.StatusBadRequest:
return errors.TypeInvalidInput.String()
case http.StatusNotFound:
return errors.TypeNotFound.String()
case http.StatusConflict:
return errors.TypeAlreadyExists.String()
case http.StatusUnauthorized:
return errors.TypeUnauthenticated.String()
case http.StatusNotImplemented:
return errors.TypeUnsupported.String()
case http.StatusForbidden:
return errors.TypeForbidden.String()
case statusClientClosedConnection:
return errors.TypeCanceled.String()
case http.StatusGatewayTimeout:
return errors.TypeTimeout.String()
case http.StatusUnavailableForLegalReasons:
return errors.TypeLicenseUnavailable.String()
case http.StatusTooManyRequests:
return errors.TypeTooManyRequests.String()
default:
return errors.TypeInternal.String()
}
}
func Error(rw http.ResponseWriter, cause error) {
// Derive the http code from the error type
t, _, _, _, _, _ := errors.Unwrapb(cause)
httpCode := http.StatusInternalServerError
switch t {
case errors.TypeInvalidInput:
httpCode = http.StatusBadRequest
case errors.TypeNotFound:
httpCode = http.StatusNotFound
case errors.TypeAlreadyExists:
httpCode = http.StatusConflict
case errors.TypeUnauthenticated:
httpCode = http.StatusUnauthorized
case errors.TypeUnsupported:
httpCode = http.StatusNotImplemented
case errors.TypeForbidden:
httpCode = http.StatusForbidden
case errors.TypeCanceled:
httpCode = statusClientClosedConnection
case errors.TypeTimeout:
httpCode = http.StatusGatewayTimeout
case errors.TypeFatal:
httpCode = http.StatusInternalServerError
case errors.TypeLicenseUnavailable:
httpCode = http.StatusUnavailableForLegalReasons
case errors.TypeTooManyRequests:
httpCode = http.StatusTooManyRequests
}
body, err := json.Marshal(&ErrorResponse{Status: StatusError.s, Error: errors.AsJSON(cause)})
if err != nil {
// this should never be the case
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
// Retry-After carries the explicit delay declared via
// errors.WithRetryAfter. Set it before WriteHeader so headers go on the wire.
d := errors.RetryDelayOf(cause)
if d.Seconds() > 0 {
rw.Header().Set("Retry-After", strconv.Itoa(int(math.Ceil(d.Seconds()))))
}
rw.Header().Set("Content-Type", "application/json")
rw.WriteHeader(httpCode)
_, _ = rw.Write(body)
}