{"id":21744051,"url":"https://github.com/telefonica/govice","last_synced_at":"2025-07-24T23:41:34.001Z","repository":{"id":66221871,"uuid":"110513478","full_name":"Telefonica/govice","owner":"Telefonica","description":"Golang library to develop a production-like service","archived":false,"fork":false,"pushed_at":"2018-10-04T11:11:21.000Z","size":43,"stargazers_count":8,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-13T05:08:02.422Z","etag":null,"topics":["config","go","golang","json-schema","logger","logging","service","validation"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Telefonica.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-11-13T07:19:16.000Z","updated_at":"2022-10-16T13:28:10.000Z","dependencies_parsed_at":"2023-07-04T07:09:10.277Z","dependency_job_id":null,"html_url":"https://github.com/Telefonica/govice","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/Telefonica/govice","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Telefonica%2Fgovice","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Telefonica%2Fgovice/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Telefonica%2Fgovice/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Telefonica%2Fgovice/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Telefonica","download_url":"https://codeload.github.com/Telefonica/govice/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Telefonica%2Fgovice/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266923038,"owners_count":24006996,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-24T02:00:09.469Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["config","go","golang","json-schema","logger","logging","service","validation"],"created_at":"2024-11-26T07:09:50.952Z","updated_at":"2025-07-24T23:41:33.902Z","avatar_url":"https://github.com/Telefonica.png","language":"Go","readme":"[![Build Status](https://api.travis-ci.org/Telefonica/govice.svg?branch=master)](https://travis-ci.org/Telefonica/govice) [![Coverage Status](https://coveralls.io/repos/Telefonica/govice/badge.svg?branch=master\u0026service=github)](https://coveralls.io/github/Telefonica/govice?branch=master) [![](https://godoc.org/github.com/Telefonica/govice?status.svg)](http://godoc.org/github.com/Telefonica/govice) [![](http://goreportcard.com/badge/Telefonica/govice)](http://goreportcard.com/report/Telefonica/govice)\n\n# govice\n\nLibraries to **serve and protect** your services implemented in golang.\n\nIt provides the following functionality:\n\n - **Configuration**. Read and marshal the configuration read from a JSON file into a struct, and override it with environment variables.\n - **Validation**. Validate your configuration and requests with JSON schemas.\n - **Logging**. Log in JSON format with custom context objects.\n - **Middlewares**. Some http.HandlerFunc middlewares (e.g. to log your requests and responses automatically).\n - **Errors and alarms**.\n\nSee **examples** directory with executable applications of these features. The **service** example is a combination of all the features provided by govice.\n\n## Configuration\n\nThe approach selected by govice to configure an application/service is based on a default configuration (using a JSON file) that can be override partially with environment variables. This approach is compliant with [The Twelve-Factor App](https://12factor.net/config). It is also very convenient when working with docker containers.\n\nThe following example loads **config.json** file into a **Config** struct and overrides the values with environment variables (if registered).\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/Telefonica/govice\"\n)\n\ntype config struct {\n\tAddress  string `json:\"address\" env:\"ADDRESS\"`\n\tBasePath string `json:\"basePath\" env:\"BASE_PATH\"`\n\tLogLevel string `json:\"logLevel\" env:\"LOG_LEVEL\"`\n}\n\nfunc main() {\n\tvar cfg config\n\tif err := govice.GetConfig(\"config.json\", \u0026cfg); err != nil {\n\t\tpanic(\"Invalid configuration.\", err)\n\t}\n\tfmt.Printf(\"%+v\\n\", cfg)\n}\n```\n\nThe function `func GetConfig(configFile string, cfg interface{}) error` receives two parameters: a) path to the JSON configuration file (relative to the execution directory), b) reference to the configuration instance.\n\nThe configuration struct uses struct tags to map each field with a JSON element (using the tag **json**) and/or and environment variable (using the tag **env**). The **env** struct tag is implemented by [github.com/caarlos0/env](https://github.com/caarlos0/env) dependency.\n\n## Validation\n\nThe govice validation is based on [JSON schemas](http://json-schema.org/) with the library [github.com/xeipuuv/gojsonschema](https://github.com/xeipuuv/gojsonschema). Its main goal is to avoid including this logic as part of the code. This separation of concerns makes the source code more readable and easier to maintain it.\n\nIt is recommended to validate any input to the service: a) configuration, b) requests to our service, c) responses received by our clients. Validation leads to safeness and robusness.\n\nThe following example extends the configuration example to validate the configuration:\n\n```go\nfunc main() {\n\tvar cfg Config\n\tif err := govice.GetConfig(\"config.json\", \u0026cfg); err != nil {\n\t\tos.Exit(1)\n\t}\n\tfmt.Prinft(\"%+v\", cfg)\n\n\tvalidator := govice.NewValidator()\n\tif err := validator.LoadSchemas(\"schemas\"); err != nil {\n\t\tpanic(err)\n\t}\n\tif err := validator.ValidateConfig(\"config\", \u0026cfg); err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(\"Configuration validated successfully\")\n}\n```\n\n`func (v *Validator) LoadSchemas(schemasDir string) error` loads all the JSON schemas located in the `schemasDir` directory. Note that this directory may be relative to the execution directory. Each JSON schemas is loaded and indexed with the file name removing the **json** extension. For example, a JSON schema stored as **schemas/config.json** is loaded with the key **config**.\n\nThen it is possible to validate the configuration (stored in a struct) against a JSON schema (using as key the JSON schema filename without extension). `func (v *Validator) ValidateConfig(schemaName string, cfg interface{}) error` validates a configuration object and generates errors aligned to configuration.\n\nThe `Validator` type also provides other methods to validate requests, objects or arrays:\n\n - `func (v *Validator) ValidateRequestBody(schemaName string, r *http.Request, o interface{}) error`. Validates the request body against a JSON schema and unmarshals it into an object.\n - `func (v *Validator) ValidateSafeRequestBody(schemaName string, r *http.Request, o interface{}) error`. As ValidateRequestBody, it also validates the request body against a JSON schema and unmarshals it into an object, but it maintains the request body to be read afterwards. This can be useful if it is required to forward the same request via proxy.\n - `func (v *Validator) ValidateObject(schemaName string, data interface{}) error`. It validates an object against a JSON schema.\n - `func (v *Validator) ValidateBytes(schemaName string, data []byte, o interface{}) error`. It reads a byte array, validates it against a JSON schema, and unmarshall it. This method is used by both **ValidateRequestBody** and **ValidateSafeRequestBody**.\n\n## Logging\n\nLogging writes log records to console using a JSON format to make easier that log aggregators (e.g. splunk) process them.\n\nLogging requires to create an instance of **Logger** (e.g. with `func NewLogger() *Logger`). It is possible to set a log level: `func (l *Logger) SetLevel(levelName string)`. Possible log levels are: **DEBUG**, **INFO**, **WARN**, **ERROR**, and **FATAL**.\n\nThe following fields are always written:\n\n| Field | Description |\n|---|---|\n| time | Timestamp when the log record was registered |\n| lvl | Log level: **DEBUG**, **INFO**, **WARN**, **ERROR**, and **FATAL**. |\n| msg | Log message |\n\nThese fields can be enhanced by using log contexts. A log context includes additional fields in the log record. There are 2 different log contexts: a) context at logger instance which is set with `func (l *Logger) SetLogContext(context interface{})`, and b) context at log record. Log contexts are structs that are marshalled into the log record (it is required to use the json struct tags to make them be marshalled).\n\nEach log level provides two methods: with and without log context (note that a log context at logger instance is complementary).\n\n| Level | Log without context | Log with context |\n| ----- | ------------------- | ---------------- |\n| DEBUG | `func (l *Logger) Debug(message string, args ...interface{})` | `func (l *Logger) DebugC(context interface{}, message string, args ...interface{})` |\n| INFO | `func (l *Logger) Info(message string, args ...interface{})` | `func (l *Logger) InfoC(context interface{}, message string, args ...interface{})` |\n| WARN | `func (l *Logger) Warn(message string, args ...interface{})` | `func (l *Logger) WarnC(context interface{}, message string, args ...interface{})` |\n| ERROR | `func (l *Logger) Error(message string, args ...interface{})` | `func (l *Logger) ErrorC(context interface{}, message string, args ...interface{})` |\n| FATAL | `func (l *Logger) Fatal(message string, args ...interface{})` | `func (l *Logger) FatalC(context interface{}, message string, args ...interface{})` |\n\nThere are several context struct defined in govice to work with HTTP:\n\n```go\n// LogContext represents the log context for a base service.\ntype LogContext struct {\n\tTransactionID string `json:\"trans,omitempty\"`\n\tCorrelator    string `json:\"corr,omitempty\"`\n\tOperation     string `json:\"op,omitempty\"`\n\tService       string `json:\"svc,omitempty\"`\n\tComponent     string `json:\"comp,omitempty\"`\n\tUser          string `json:\"user,omitempty\"`\n\tRealm         string `json:\"realm,omitempty\"`\n\tAlarm         string `json:\"alarm,omitempty\"`\n}\n\n// ReqLogContext is a complementary LogContext to log information about the request (e.g. path).\ntype ReqLogContext struct {\n\tMethod     string `json:\"method,omitempty\"`\n\tPath       string `json:\"path,omitempty\"`\n\tRemoteAddr string `json:\"remoteaddr,omitempty\"`\n}\n\n// RespLogContext is a complementary LogContext to log information about the response (e.g. status code).\ntype RespLogContext struct {\n\tStatus   int    `json:\"status,omitempty\"`\n\tLatency  int    `json:\"latency,omitempty\"`\n\tLocation string `json:\"location,omitempty\"`\n}\n```\n\nThe following example demonstrates how to use the govice logger and the contexts:\n\n```go\npackage main\n\nimport (\n\t\"time\"\n\n\t\"github.com/Telefonica/govice\"\n)\n\ntype demoContext struct {\n\tFeature int `json:\"feat,omitempty\"`\n}\n\nfunc main() {\n\t// Force UTC time zone (used in time field of the log records)\n\ttime.Local = time.UTC\n\t// Create the context for the logger instance\n\tctxt := govice.LogContext{Service: \"logger\", Component: \"demo\"}\n\n\tlogger := govice.NewLogger()\n\tlogger.SetLogContext(ctxt)\n\tlogger.Info(\"Logging without context\")\n\tlogger.Warn(\"Logging with %d %s\", 2, \"arguments\")\n\n\trecordCtxt := \u0026demoContext{Feature: 3}\n\tlogger.InfoC(recordCtxt, \"Logging with context\")\n}\n```\n\nThe output of the previous command is:\n\n```\n{\"time\":\"2017-11-12T23:29:55.929Z\",\"lvl\":\"INFO\",\"svc\":\"logger\",\"comp\":\"demo\",\"msg\":\"Logging without context\"}\n{\"time\":\"2017-11-12T23:29:55.929Z\",\"lvl\":\"WARN\",\"svc\":\"logger\",\"comp\":\"demo\",\"msg\":\"Logging with 2 arguments\"}\n{\"time\":\"2017-11-12T23:29:55.929Z\",\"lvl\":\"INFO\",\"svc\":\"logger\",\"comp\":\"demo\",\"feat\":3,\"msg\":\"Logging with context\"}\n```\n\nNote that the logger context supports other data types beyond strings. This is really important to build up metrics based on logs.\n\nThere are additional utilities to dump requests and responses (in **DEBUG** level):\n\n| Method | Description |\n| ------ | ----------- |\n| `func (l *Logger) DebugResponse(message string, r *http.Response)` | Dump a response |\n| `func (l *Logger) DebugRequest(message string, r *http.Request)` | Dump a request received by the app |\n| `func (l *Logger) DebugRequestOut(message string, r *http.Request)` | Dump a request generated by the app |\n\nNote that these methods already have a version with context (e.g. **DebugResponseC**).\n\n## Middlewares\n\n| Middleware | Description |\n| ---------- | ----------- |\n| WithLogContext(ctx *LogContext) | Creates a logger (stored in the context of the request) and prepares the transactionID and correlator in the log context. It is also responsible to include the HTTP header for the correlator in both request and response. |\n| WithLog | It logs the request and response |\n| WithMethodNotAllowed(allowedMethods []string) | Generates a response with the **Allow** header with the allowed HTTP methods. |\n| WithNotFound | Replies with a 404 error |\n\nThere are some important utilities related to **WithLogContext**. `func GetLogger(r *http.Request) *Logger` returns the logger created by the **WithLogContext** middleware. `func GetLogContext(r *http.Request) *LogContext` returns the log context from the previous logger.\n\nThe following example creates a web server where every request and response is logged in the console. This is achieved by concatenating **WithLogContext** and **WithLog** middlewares.\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/Telefonica/govice\"\n)\n\nfunc handler(w http.ResponseWriter, r *http.Request) {\n\tfmt.Fprintln(w, \"Hello world\")\n}\n\nfunc main() {\n\t// Force UTC time zone (used in time field of the log records)\n\ttime.Local = time.UTC\n\t// Create the context for the logger instance\n\tctxt := govice.LogContext{Service: \"logger\", Component: \"demo\"}\n\n\thttp.HandleFunc(\"/\", govice.WithLogContext(\u0026ctxt)(govice.WithLog(handler)))\n\thttp.ListenAndServe(\":8080\", nil)\n}\n```\n\nThis example creates the following log records:\n\n```\n{\"time\":\"2017-11-13T08:01:51.335Z\",\"lvl\":\"INFO\",\"trans\":\"e7fc31ab-c848-11e7-8ed5-186590e007bb\",\"corr\":\"e7fc31ab-c848-11e7-8ed5-186590e007bb\",\"svc\":\"logger\",\"comp\":\"demo\",\"method\":\"GET\",\"path\":\"/\",\"remoteaddr\":\"[::1]:49636\",\"msg\":\"Request\"}\n{\"time\":\"2017-11-13T08:01:51.335Z\",\"lvl\":\"INFO\",\"trans\":\"e7fc31ab-c848-11e7-8ed5-186590e007bb\",\"corr\":\"e7fc31ab-c848-11e7-8ed5-186590e007bb\",\"svc\":\"logger\",\"comp\":\"demo\",\"status\":200,\"msg\":\"Response\"}\n```\n\nNote that the log context passed to **WithLogContext** middleware must follow the type **govice.LogContext**. This is required because the middleware sets the transactionID and correlator in this context.\n\nThe `Pipeline` simplifies the creation of a list of middlewares. The previous example would be:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/Telefonica/govice\"\n)\n\nfunc handler(w http.ResponseWriter, r *http.Request) {\n\tfmt.Fprintln(w, \"Hello world\")\n}\n\nfunc main() {\n\t// Force UTC time zone (used in time field of the log records)\n\ttime.Local = time.UTC\n\t// Create the context for the logger instance\n\tctxt := govice.LogContext{Service: \"logger\", Component: \"demo\"}\n\t// Create the list of middlewares for the pipeline (excluding the last handler)\n\tmws := []func(http.HandlerFunc) http.HandlerFunc{\n\t\tgovice.WithLogContext(\u0026ctxt),\n\t\tgovice.WithLog,\n\t}\n\thttp.HandleFunc(\"/\", govice.Pipeline(mws, handler))\n\thttp.ListenAndServe(\":8080\", nil)\n}\n```\n\n## Errors and alarms\n\nThis library defines some custom errors. Errors store information for logging, and to generate the HTTP response.\n\n```go\ntype Error struct {\n\tMessage     string `json:\"-\"`\n\tAlarm       string `json:\"-\"`\n\tStatus      int    `json:\"-\"`\n\tCode        string `json:\"error\"`\n\tDescription string `json:\"error_description,omitempty\"`\n}\n```\n\n| Field | Description |\n| ----- | ----------- |\n| Message | Message to be logged |\n| Alarm | It identifies an optional alarm identifier to be included in the log record if the error requires to trigger an alarm for ops. |\n| Status | Status code of the HTTP response |\n| Code | Error identifier (or error type). It corresponds to the **error** field in the JSON response body |\n| Description | Description of the error (optional). It corresponds to the **error_description** field in the JSON response body |\n\nThe format of the response body complies with the error format defined by [OAuth2 standard](https://tools.ietf.org/html/rfc6749#section-5.2).\n\nThe function `func ReplyWithError(w http.ResponseWriter, r *http.Request, err error)` has two responsibilities:\n\n - It generates a HTTP response using a standard error. If the error is of type **govice.Error**, then it is casted to retrieve all the information; otherwise, it replies with a server error.\n - It also logs the error using the logger in the request context. Note that it depends on the **WithLogContext** middleware. If the status code associated to the error is 4xx, then it is logged with **INFO** level; otherwise, with **ERROR** level. If the error contains an alarm identifier, it is also logged.\n\n## Additional utilities\n\nIt provides a simple utility to create a HTTP JSON response by following 2 steps:\n - Add the `Content-Type` header to `application/json`\n - Marshal a golang type to JSON. If the marshalling fails, it replies with a govice error.\n\n```go\nfunc handler(w http.ResponseWriter, r *http.Request) {\n\t// Object to be serialized to JSON in the HTTP response\n\tresp := govice.LogContext{Service: \"logger\", Component: \"demo\"}\n\tgovice.WriteJSON(w, r, \u0026resp)\n}\n```\n\n## License\n\nCopyright 2017 [Telefónica Investigación y Desarrollo, S.A.U](http://www.tid.es)\n\nLicensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftelefonica%2Fgovice","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftelefonica%2Fgovice","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftelefonica%2Fgovice/lists"}