Mortar is a GO framework/library for building gRPC (and REST) web services.
Mortar is a GO framework/library for building gRPC (and REST) web services. Mortar has out-of-the-box support for configuration, application metrics, logging, tracing, profiling, dependency injection and more. While it comes with predefined defaults, Mortar gives you total control to fully customize it. |
---|
Clone this demo repository to better understand some of Mortar capabilities.
When you done, read the documentation.
To help you bootstrap your services with Mortar here you can find a template. Read its README first.
Bundled Grpc-Gateway (REST Reverse-Proxy).
Dependency Injection using Uber-FX.
Pimped *http.Client
with interceptors support.
Abstract support for Logging, Configuration, Tracing and Monitoring libraries. Use provided wrappers or your own.
Internal HTTP Handlers
http://.../debug/pprof
http://.../debug/*
http://.../self/config
http://.../self/build
http://.../health
Server/Client Interceptors both for gRPC and HTTP, you can choose which to use and/or add your own.
Some examples
ctx.Context
via gRPC incoming Metadata....and more.
Logs have Tracing Information traceId=6ff7e7e38d1e86f
across services
Also visible in Jaeger traceId=6ff7e7e38d1e86f
if it's sampled.
*http.Client
Interceptors, so you canAdd request and response info to Trace
Log/Dump requests and/or responses when http request fails.
return func(req *http.Request, handler client.HTTPHandler) (resp *http.Response, err error) {
var reqBytes, respBytes []byte
// If the response is Bad Request, log both Request and Response
reqBytes, _ = httputil.DumpRequestOut(req, true) // it can be nil and it's ok
if resp, err = handler(req); err == nil && resp.StatusCode >= http.StatusBadRequest {
respBytes, _ = httputil.DumpResponse(resp, true) // it can be nil
logger.WithError(fmt.Errorf("http request failed")).
WithField("status",resp.StatusCode).
Warn(req.Context(), "\nRequest:\n%s\n\nResponse:\n%s\n", reqBytes, respBytes)
}
return
}
Alter requests and/or responses (useful in Tests)
func(*http.Request, clientInt.HTTPHandler) (*http.Response, error) {
// special case, don't go anywhere just return the response
return &http.Response{
Status: "200 OK",
StatusCode: 200,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
ContentLength: 11,
Body: ioutil.NopCloser(strings.NewReader("car painted")),
}, nil
}
Export to either Prometheus/Datadog/statsd/etc, it's your choice. Mortar only provides the Interface and also caches the metrics so you don't have to.
counter := w.deps.Metrics.WithTags(monitor.Tags{
"color": request.GetDesiredColor(),
"success": fmt.Sprintf("%t", err == nil),
}).Counter("paint_desired_color", "New paint color for car")
counter.Inc()
counter
is actually a singleton, uniqueness calculated here
For more information about Mortar Monitoring read here.
/debug/pprof
and other useful handlers
config_test.yml
during tests to override values in config.yml
, it saves time.There are some features not listed here, please check the Documentation for more.
Mortar is not a drop-in replacement.
It's important to read its Documentation first.