blob: 4d18f3f8ca4b5ac45bbe314f49ae2865d560fd58 [file] [log] [blame] [view]
# Style Guide
CeresMeta is written in Golang so the basic code style we adhere to is the [CodeReviewComments](https://github.com/golang/go/wiki/CodeReviewComments).
Besides the [CodeReviewComments](https://github.com/golang/go/wiki/CodeReviewComments), there are also some custom rules for the project:
- Error Handling
- Logging
## Error Handling
### Principles
- Global error code:
- Any error defined in the repo should be assigned an error code,
- An error code can be used by multiple different errors,
- The error codes are defined in the single global package [coderr](https://github.com/CeresDB/ceresmeta/tree/main/pkg/coderr).
- Construct: define leaf errors on package level (often in a separate `error.go` file) by package [coderr](https://github.com/CeresDB/ceresmeta/tree/main/pkg/coderr).
- Wrap: wrap errors by `errors.WithMessage` or `errors.WithMessagef`.
- Check: test the error identity by calling `coderr.Is`.
- Log: only log the error on the top level package.
- Respond: respond the `CodeError`(defined in package [coderr](https://github.com/CeresDB/ceresmeta/tree/main/pkg/coderr)) unwrapped by `errors.Cause` to client on service level.
### Example
`errors.go` in the package `server`:
```go
var ErrStartEtcd = coderr.NewCodeError(coderr.Internal, "start embed etcd")
var ErrStartEtcdTimeout = coderr.NewCodeError(coderr.Internal, "start etcd server timeout")
```
`server.go` in the package `server`:
```go
func (srv *Server) startEtcd() error {
etcdSrv, err := embed.StartEtcd(srv.etcdCfg)
if err != nil {
return ErrStartEtcd.WithCause(err)
}
newCtx, cancel := context.WithTimeout(srv.ctx, srv.cfg.EtcdStartTimeout())
defer cancel()
select {
case <-etcdSrv.Server.ReadyNotify():
case <-newCtx.Done():
return ErrStartEtcdTimeout.WithCausef("timeout is:%v", srv.cfg.EtcdStartTimeout())
}
return nil
}
```
`main.go` in the package `main`:
```go
func main() {
err := srv.startEtcd()
if err != nil {
return
}
if coderr.Is(err, coderr.Internal) {
log.Error("internal error")
}
cerr, ok := err.(coderr.CodeError)
if ok {
log.Error("found a CodeError")
} else {
log.Error("not a CodeError)
}
return
}
```
## Logging
### Principles
- Structured log by [zap](https://github.com/uber-go/zap).
- Use the package `github.com/ceresmeta/pkg/log` which is based on [zap](https://github.com/uber-go/zap).
- Create local logger with common fields if necessary.
### Example
Normal usage:
```go
import "github.com/ceresmeta/pkg/log"
func main() {
if err := srv.Run(); err != nil {
log.Error("fail to run server", zap.Error(err))
return
}
}
```
Local logger:
```go
import "github.com/ceresmeta/pkg/log"
type lease struct {
ID int64
logger *zap.Logger
}
func NewLease(ID int64) *lease {
logger := log.With(zap.Int64("lease-id", ID))
return &lease {
ID,
logger,
}
}
func (l *lease) Close() {
l.logger.Info("lease is closed")
l.ID = 0
}
```