/
server.go
106 lines (91 loc) · 3.04 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package httpserver
import (
"compress/gzip"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
datadog "github.com/DataDog/opencensus-go-exporter-datadog"
"github.com/pkg/errors"
"go.opencensus.io/plugin/ochttp"
"go.opencensus.io/stats/view"
"go.opencensus.io/trace"
http_model "github.com/SKF/go-utility/http-model"
"github.com/SKF/go-utility/log"
)
func StartHealthServer(port string) {
http.HandleFunc("/health", func(w http.ResponseWriter, req *http.Request) {
WriteJSONResponse(req.Context(), w, req, http.StatusOK, []byte(`{"status": "ok"}`))
})
log.Infof("Starting health server on port %s", port)
if err := http.ListenAndServe(fmt.Sprintf(":%s", port), nil); err != nil { // nolint: gosec
log.WithError(err).Error("ListenAndServe")
}
}
func SetupDatadogInstrumentation(service, awsRegion, awsAccountID, stage string) *datadog.Exporter {
ddTracer, err := datadog.NewExporter(datadog.Options{
Service: service,
GlobalTags: map[string]interface{}{
"aws_region": awsRegion,
"aws_account_id": awsAccountID,
"env": stage,
},
})
if err != nil {
log.Fatalf("Failed to create the Datadog exporter: %v", err)
}
if err := view.Register(ochttp.DefaultServerViews...); err != nil {
log.Fatalf("Failed to register server views for HTTP metrics: %v", err)
}
view.RegisterExporter(ddTracer)
trace.RegisterExporter(ddTracer)
// Allow Datadog to calculate APM metrics and do the sampling.
trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
return ddTracer
}
func UnmarshalRequest(body io.ReadCloser, v interface{}) (err error) {
defer body.Close()
if err = json.NewDecoder(body).Decode(v); err != nil {
err = errors.Wrap(err, "failed to unmarshal request body")
}
return
}
func MarshalAndWriteJSONResponse(ctx context.Context, w http.ResponseWriter, r *http.Request, code int, v interface{}) {
response, err := json.Marshal(v)
if err != nil {
log.WithError(err).
WithTracing(ctx).
WithField("type", fmt.Sprintf("%T", v)).
Error("Failed to marshal response body")
response = http_model.ErrResponseInternalServerError
}
WriteJSONResponse(ctx, w, r, code, response)
}
// Don't gzip body if smaller than one packet, as it will be transmitted as a full packet anyway.
const gzipMinBodySize = 1400
func WriteJSONResponse(ctx context.Context, w http.ResponseWriter, r *http.Request, code int, body []byte) {
w.Header().Set("Content-Type", "application/json")
var err error
if r != nil && strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") && len(body) > gzipMinBodySize {
w.Header().Set("Content-Encoding", "gzip")
w.WriteHeader(code)
gz := gzip.NewWriter(w)
defer gz.Close()
_, err = gz.Write(body)
} else {
w.WriteHeader(code)
_, err = w.Write(body)
}
if err != nil {
log.WithError(err).
WithTracing(ctx).
Error("Failed to write response")
}
}
func StatusNotFoundHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
WriteJSONResponse(r.Context(), w, r, http.StatusNotFound, http_model.ErrResponseNotFound)
})
}