Documentation ¶
Overview ¶
Package app provides the application service model and platform for building message driven concurrent and distributed services.
Design Principles
High performance. Prove it through benchmarking.
Concurrency safe. Always design the service to be safely used by concurrent goroutines. - Prefer immutability - Prefer channels - use locks as a last resort
Services define their dependencies as functions.
One service per package, i.e., the package is the service. Public functions exposed by the package are the service interface.
Each service package will initialize itself automatically, i.e., via package init() functions. Each service package will register itself with the app.Application service.
Graceful application shutdown : when the application is killed, all registered services will be killed. The application will wait until all services are dead before exiting. - The following OS signals will trigger graceful application shutdown : SIGINT, SIGTERM, SIGQUIT
All key components will be assigned a unique numeric id (uint64) for tracking and traceability purposes. - DomainID - AppID - ReleaseID - app release id - ServiceID - ErrorID - LogEventID
Most systems prefer instead to define a symbolic global namespace , but this would have some important disadvantages:
Programmers often feel the need to change symbolic names and organization in order to make their code cleaner, but the renamed code should still work with existing encoded data.
It’s easy for symbolic names to collide, and these collisions could be hard to detect in a large distributed system with many different binaries.
Fully-qualified type names may be large and waste space when transmitted on the wire.
Config is typesafe and secure. - Each service will define its config via capnp. The config will be self contained. - Configurations will be encrypted. - Docker secrets (https://docs.docker.com/engine/swarm/secrets/) will be supported
RPC network communication will be secured via mutual TLS - capnp RPC will be supported
Index ¶
- Constants
- Variables
- func Alive() bool
- func Dead() <-chan struct{}
- func DumpAllStacks(out io.Writer)
- func Dying() <-chan struct{}
- func IsError(err error, errorID ErrorID) bool
- func Kill()
- func LogLevel() zerolog.Level
- func Logger() zerolog.Logger
- func MarshalCapnpMessage(msg *capnp.Message, out io.Writer) error
- func MetricSpecLabels(serviceID ServiceID) prometheus.Labels
- func MetricsServiceSpec() config.MetricsServiceSpec
- func PrometheusName(metricSpec *MetricSpec) string
- func Reset()
- func ResetWithConfigDir(configDir string)
- func StartedOn() time.Time
- func UnmarshalCapnpMessage(in io.Reader) (*capnp.Message, error)
- type AppConfig
- func (a AppConfig) Config(id ServiceID) (*capnp.Message, error)
- func (a AppConfig) ConfigDir() string
- func (a AppConfig) ConfigDirExists() bool
- func (a AppConfig) ConfigServiceIDs() ([]ServiceID, error)
- func (a AppConfig) ServiceConfigExists(id ServiceID) bool
- func (a AppConfig) ServiceConfigID(id ServiceID) string
- func (a AppConfig) ServiceConfigPath(id ServiceID) string
- func (a AppConfig) SetConfigDir(dir string)
- type AppHealthChecks
- func (a AppHealthChecks) HealthCheckIDs() []HealthCheckID
- func (a AppHealthChecks) HealthCheckResult(id HealthCheckID) (HealthCheckResult, error)
- func (a AppHealthChecks) HealthCheckResults() map[HealthCheckID]HealthCheckResult
- func (a AppHealthChecks) HealthCheckSpec(id HealthCheckID) *HealthCheckSpec
- func (a AppHealthChecks) PauseHealthCheck(id HealthCheckID) bool
- func (a AppHealthChecks) PausedHealthChecks() []HealthCheckID
- func (a AppHealthChecks) Register(id HealthCheckID, healthCheckFunc HealthCheck) error
- func (a AppHealthChecks) Registered(id HealthCheckID) bool
- func (a AppHealthChecks) ResumeHealthCheck(id HealthCheckID) error
- func (a AppHealthChecks) Run(id HealthCheckID) (HealthCheckResult, error)
- type AppID
- type AppMetricRegistry
- func (a AppMetricRegistry) Counter(serviceId ServiceID, metricID MetricID) *CounterMetric
- func (a AppMetricRegistry) CounterMetricIds(serviceId ServiceID) []MetricID
- func (a AppMetricRegistry) CounterMetricsIdsPerService() map[ServiceID][]MetricID
- func (a AppMetricRegistry) CounterVector(serviceId ServiceID, metricID MetricID) *CounterVectorMetric
- func (a AppMetricRegistry) CounterVectorMetricIds(serviceId ServiceID) []MetricID
- func (a AppMetricRegistry) CounterVectorMetricsIdsPerService() map[ServiceID][]MetricID
- func (a AppMetricRegistry) Gauge(serviceId ServiceID, metricID MetricID) *GaugeMetric
- func (a AppMetricRegistry) GaugeMetricIds(serviceId ServiceID) []MetricID
- func (a AppMetricRegistry) GaugeMetricsByService() map[ServiceID][]MetricID
- func (a AppMetricRegistry) GaugeVector(serviceId ServiceID, metricID MetricID) *GaugeVectorMetric
- func (a AppMetricRegistry) GaugeVectorMetricIds(serviceId ServiceID) []MetricID
- func (a AppMetricRegistry) GaugeVectorMetricsByService() map[ServiceID][]MetricID
- func (a AppMetricRegistry) Histogram(serviceId ServiceID, metricID MetricID) *HistogramMetric
- func (a AppMetricRegistry) HistogramMetricIds(serviceId ServiceID) []MetricID
- func (a AppMetricRegistry) HistogramMetricsByService() map[ServiceID][]MetricID
- func (a AppMetricRegistry) HistogramVector(serviceId ServiceID, metricID MetricID) *HistogramVectorMetric
- func (a AppMetricRegistry) HistogramVectorMetricIds(serviceId ServiceID) []MetricID
- func (a AppMetricRegistry) HistogramVectorMetricsByService() map[ServiceID][]MetricID
- type AppServices
- type CommandServer
- type CounterMetric
- type CounterMetricSpec
- type CounterVectorMetric
- type CounterVectorMetricSpec
- type DomainID
- type ErrSpec
- type Error
- func AppNotAliveError() *Error
- func ConfigError(serviceID ServiceID, err error, message string) *Error
- func HealthCheckAlreadyRegisteredError(healthCheckID HealthCheckID) *Error
- func HealthCheckKillTimeoutError(healthCheckID HealthCheckID) *Error
- func HealthCheckNotAliveError(healthCheckID HealthCheckID) *Error
- func HealthCheckNotRegisteredError(healthCheckID HealthCheckID) *Error
- func HealthCheckTimeoutError(healthCheckID HealthCheckID) *Error
- func IllegalArgumentError(message string) *Error
- func InvalidLogLevelError(message string) *Error
- func NewBug(cause error, message string, serviceID ServiceID, ctx interface{}, ...) *Error
- func NewError(cause error, message string, errSpec ErrSpec, serviceID ServiceID, ...) *Error
- func ServiceAlreadyRegisteredError(serviceID ServiceID) *Error
- func ServiceInitError(serviceID ServiceID, err error) *Error
- func ServiceNotAliveError(serviceID ServiceID) *Error
- func ServiceNotAvailableError(serviceID ServiceID) *Error
- func ServiceNotRegisteredError(serviceID ServiceID) *Error
- func ServiceShutdownError(serviceID ServiceID, err error) *Error
- type ErrorID
- type ErrorSeverity
- type ErrorType
- type GaugeMetric
- type GaugeMetricSpec
- type GaugeVectorMetric
- type GaugeVectorMetricSpec
- type HealthCheck
- type HealthCheckID
- type HealthCheckResult
- type HealthCheckSpec
- type HistogramMetric
- type HistogramMetricSpec
- type HistogramVectorMetric
- type HistogramVectorMetricSpec
- type InstanceID
- type LogEventID
- type MetricID
- type MetricSpec
- type MetricVectorSpec
- type ReleaseID
- type Service
- type ServiceCommandChannel
- type ServiceErrSpec
- type ServiceID
Constants ¶
const ( // for errors that have not been anticipated or not yet customized to your system ErrorType_BUG = ErrorType(iota) // e.g. broken network connections, IO errors, service not available, etc ErrorType_KNOWN_EDGE_CASE // misconfiguration or missing configuration ErrorType_Config )
ErrorType enum values
const ( ErrorSeverity_LOW = ErrorSeverity(iota) ErrorSeverity_MEDIUM ErrorSeverity_HIGH // should cause the app to terminate, i.e. trigger a panic ErrorSeverity_FATAL )
ErrorSeverity enum values
const ( HEALTHCHECK_SERVICE_ID = ServiceID(0xe105d0909fcc5ec5) HEALTHCHECK_METRIC_LABEL = "healthcheck" // the label value will be the HealthCheckID in hex format HEALTHCHECK_METRIC_ID = MetricID(0x844d7830332bffd3) HEALTHCHECK_RUN_DURATION_METRIC_ID = MetricID(0xcd4260d6e89ad9c6) DEFAULT_HEALTHCHECK_RUN_INTERVAL = 5 * time.Minute DEFAULT_HEALTHCHECK_RUN_TIMEOUT = 5 * time.Second HEALTHCHECK_ID_LOG_FIELD = "healthcheck" )
healthcheck constants
const ( APP_STARTED = LogEventID(0xa482715a50d67a5f) APP_STOPPING = LogEventID(0xbcdae48c0cb8936e) APP_STOPPING_TIMEOUT = LogEventID(0xaa7744d8a20d857a) APP_STOPPED = LogEventID(0xdd0c7775e42d7841) APP_RESET = LogEventID(0xee317bbb0fe0fafe) // it is the service's responsibility to log the following Service lifecycle events SERVICE_STARTING = LogEventID(0xa3c3eb887d09f9aa) SERVICE_STARTED = LogEventID(0xc27a49a4e5a2a502) // the below events are logged by the app using the service logger SERVICE_KILLED = LogEventID(0x85adf7d70dcef626) SERVICE_STOPPING = LogEventID(0x85adbb661141efce) SERVICE_STOPPING_TIMEOUT = LogEventID(0x8ed2e400ce585f16) SERVICE_STOPPED = LogEventID(0xfd843c25ce81f841) SERVICE_REGISTERED = LogEventID(0xd8f25797ffa58858) SERVICE_UNREGISTERED = LogEventID(0xa611d10b1dfc880d) METRICS_SERVICE_CONFIG_ERROR = LogEventID(0x83ad8592584d0930) METRICS_HTTP_REPORTER_SHUTDOWN_ERROR = LogEventID(0xab355f6933e9b3fe) METRICS_HTTP_REPORTER_START_ERROR = LogEventID(0xa1e82667eef867ff) METRICS_HTTP_REPORTER_SHUTDOWN_WHILE_SERVICE_RUNNING = LogEventID(0xf8c787086a35cd78) METRICS_HTTP_SERVER_STARTING = LogEventID(0xbec4ba15afa97c2b) METRICS_HTTP_SERVER_STARTED = LogEventID(0x8fab3d9ef5011368) METRICS_HTTP_SERVER_STOPPED = LogEventID(0xefd0f72bffc636f6) CONFIG_LOADING_ERR = LogEventID(0x83e927cb25aaa032) CAPNP_ERR = LogEventID(0x823407a4ed427f33) ZERO_HEALTHCHECKS = LogEventID(0x8c805853a70ec666) HEALTHCHECK_REGISTERED = LogEventID(0xb5f3142d410a2375) HEALTHCHECK_PAUSED = LogEventID(0xc7a3b54188e75210) HEALTHCHECK_RESUMED = LogEventID(0xc4dc7b2938caf3a9) HEALTHCHECK_RESULT = LogEventID(0xa68e0475cc1839be) )
log events
const ( METRICS_SERVICE_ID = ServiceID(0xe3054017c1b1d214) DEFAULT_METRICS_HTTP_PORT uint16 = 4444 )
const (
APP_SERVICE = ServiceID(0)
)
const (
CONFIG_SERVICE_ID = ServiceID(0x86813dd78fd207f5)
)
const ( // BUG is for "unknown" errors, i.e., for errors that we did not anticipate ErrorID_BUG = ErrorID(0) )
Variables ¶
var ( INFRASTRUCTURE_SERVICE_IDS = []ServiceID{ CONFIG_SERVICE_ID, METRICS_SERVICE_ID, HEALTHCHECK_SERVICE_ID, } PID = os.Getpid() HOSTNAME = func() string { name, err := os.Hostname() if err != nil { log.Panic().Err(err).Msg("os.Hostname() failed") } return name }() )
var ( Services AppServices Configs AppConfig MetricRegistry AppMetricRegistry HealthChecks AppHealthChecks )
app framework
var ( ErrSpec_AppNotAlive = ErrSpec{ErrorID: ErrorID(0xdf76e1927f240401), ErrorType: ErrorType_KNOWN_EDGE_CASE, ErrorSeverity: ErrorSeverity_FATAL} ErrSpec_ConfigFailure = ErrSpec{ErrorID: ErrorID(0xe75f1a73534f382d), ErrorType: ErrorType_Config, ErrorSeverity: ErrorSeverity_FATAL} ErrSpec_ServiceInitFailed = ErrSpec{ErrorID: ErrorID(0xec1bf26105c1a895), ErrorType: ErrorType_KNOWN_EDGE_CASE, ErrorSeverity: ErrorSeverity_FATAL} ErrSpec_ServiceShutdownFailed = ErrSpec{ErrorID: ErrorID(0xc24ac892db47da9f), ErrorType: ErrorType_KNOWN_EDGE_CASE, ErrorSeverity: ErrorSeverity_MEDIUM} ErrSpec_ServiceNotAlive = ErrSpec{ErrorID: ErrorID(0x9cb3a496d32894d2), ErrorType: ErrorType_KNOWN_EDGE_CASE, ErrorSeverity: ErrorSeverity_HIGH} ErrSpec_ServiceNotRegistered = ErrSpec{ErrorID: ErrorID(0xf34b64bac786f536), ErrorType: ErrorType_KNOWN_EDGE_CASE, ErrorSeverity: ErrorSeverity_HIGH} ErrSpec_ServiceNotAvailable = ErrSpec{ErrorID: ErrorID(0x8aae12f3016b7f50), ErrorType: ErrorType_KNOWN_EDGE_CASE, ErrorSeverity: ErrorSeverity_HIGH} ErrSpec_ServiceAlreadyRegistered = ErrSpec{ErrorID: ErrorID(0xcfd879a478f9c733), ErrorType: ErrorType_KNOWN_EDGE_CASE, ErrorSeverity: ErrorSeverity_HIGH} ErrSpec_IllegalArgument = ErrSpec{ErrorID: ErrorID(0x9d95c5fac078b82c), ErrorType: ErrorType_KNOWN_EDGE_CASE, ErrorSeverity: ErrorSeverity_HIGH} ErrSpec_InvalidLogLevel = ErrSpec{ErrorID: ErrorID(0x814a17666a94fe39), ErrorType: ErrorType_Config, ErrorSeverity: ErrorSeverity_HIGH} ErrSpec_HealthCheckAlreadyRegistered = ErrSpec{ErrorID: ErrorID(0xdbfd6d9ab0049876), ErrorType: ErrorType_KNOWN_EDGE_CASE, ErrorSeverity: ErrorSeverity_MEDIUM} ErrSpec_HealthCheckNotRegistered = ErrSpec{ErrorID: ErrorID(0xefb3ffddac690f37), ErrorType: ErrorType_KNOWN_EDGE_CASE, ErrorSeverity: ErrorSeverity_LOW} ErrSpec_HealthCheckNotAlive = ErrSpec{ErrorID: ErrorID(0xe1972916f1c18dae), ErrorType: ErrorType_KNOWN_EDGE_CASE, ErrorSeverity: ErrorSeverity_HIGH} ErrSpec_HealthCheckKillTimeout = ErrSpec{ErrorID: ErrorID(0xf4ad6052397f6858), ErrorType: ErrorType_KNOWN_EDGE_CASE, ErrorSeverity: ErrorSeverity_HIGH} ErrSpec_HealthCheckTimeout = ErrSpec{ErrorID: ErrorID(0x8257a572526e13f4), ErrorType: ErrorType_KNOWN_EDGE_CASE, ErrorSeverity: ErrorSeverity_HIGH} )
var (
ErrSpec_BUG = ErrSpec{ErrorID_BUG, ErrorType_BUG, ErrorSeverity_HIGH}
)
Functions ¶
func Dead ¶
func Dead() <-chan struct{}
Dead returns a channel that signals the app has been shutdown
func DumpAllStacks ¶
DumpAllStacks dumps all goroutine stacks to the specified writer. Used for troubleshooting purposes.
func Dying ¶
func Dying() <-chan struct{}
Dying returns a channel that is used to signal that the app has been killed and shutting down
func IsError ¶
IsError returns true if the error type is *Error and the ErrorID matches. Returns false if error is nil.
func LogLevel ¶
LogLevel returns the application log level. If the command line is parsed, then the -loglevel flag will be inspected. Valid values for -loglevel are : [DEBUG,INFO,WARN,ERROR] If not specified on the command line, then the defauly value is INFO. The log level is used to configure the log level for loggers returned via NewTypeLogger() and NewPackageLogger(). It is also used to initialize zerolog's global logger level.
func MarshalCapnpMessage ¶
MarshalCapnpMessage marshals the capnp message to the writer. The message will be compressed using zlib.
func MetricSpecLabels ¶
func MetricSpecLabels(serviceID ServiceID) prometheus.Labels
MetricSpecLabels returns the following labels:
- domain : DomainID.Hex()
- app : AppID.Hex
- svc : ServiceID.Hex()
- release : Release().Hex()
- instance : Instance().Hex()
func MetricsServiceSpec ¶
func MetricsServiceSpec() config.MetricsServiceSpec
func PrometheusName ¶
func PrometheusName(metricSpec *MetricSpec) string
PrometheusName naming convention : op_{ServiceID.Hex()}_{MetricID.Hex()} e.g., op_e49214fa20b35ba8_db722ec1bb1b5766
func Reset ¶
func Reset()
Reset is exposed only for testing purposes. Reset will :
- kill the app and then resurrect it with a new Tomb
- start the app server.
- start the app RPC server
- reset metrics
- start the metrics HTTP reporter
- reset healthchecks
func ResetWithConfigDir ¶
func ResetWithConfigDir(configDir string)
func UnmarshalCapnpMessage ¶
UnmarshalCapnpMessage unmarshals the capnp message using the specified reader. The message should have have marshalled via MarshalCapnpMessage(), i.e., it must be zlib compressed.
Types ¶
type AppConfig ¶
type AppConfig struct{}
AppConfig is used to group together config related functions
func (AppConfig) Config ¶
Config returns the config message for the specified ServiceID. The config is expected to exist at {configDir}/{ServiceID}, where the ServiceID is specified in HEX format, e.g.,
/run/secrets/0xe49214fa20b35ba8
If no config exists for the specified service, then nil is returned.
errors:
- ConfigError
func (AppConfig) ConfigDirExists ¶
ConfigDirExists returns true if the config dir exists
func (AppConfig) ConfigServiceIDs ¶
ConfigServiceIDs returns the list of ServiceID(s) for which configs exist The ServiceID is used as the config id.
func (AppConfig) ServiceConfigExists ¶
ServiceConfigExists returns true if a config exists for the specified ServiceID
func (AppConfig) ServiceConfigID ¶
ServiceConfigID is used to convert the ServiceID into the config id. The config id will be the ServiceID in HEX format.
func (AppConfig) ServiceConfigPath ¶
ServiceConfigPath returns the service config file path
func (AppConfig) SetConfigDir ¶
SetConfigDir is only exposed for testing purposes. This enables tests to setup test configurations
type AppHealthChecks ¶
type AppHealthChecks struct{}
func (AppHealthChecks) HealthCheckIDs ¶
func (a AppHealthChecks) HealthCheckIDs() []HealthCheckID
HealthCheckIDs returns the HealthCheckID(s) for the healthchecks that are currently registered. nil is returned if there are no healthchecks currently registered.
errors
- ErrServiceNotAlive
func (AppHealthChecks) HealthCheckResult ¶
func (a AppHealthChecks) HealthCheckResult(id HealthCheckID) (HealthCheckResult, error)
LastResult returns the latest HealthCheckResult from the last time the HealthCheck was run.
errors
- ErrHealthCheckNotRegistered
func (AppHealthChecks) HealthCheckResults ¶
func (a AppHealthChecks) HealthCheckResults() map[HealthCheckID]HealthCheckResult
HealthCheckResults returns a snapshot of all current HealthCheckResult(s)
func (AppHealthChecks) HealthCheckSpec ¶
func (a AppHealthChecks) HealthCheckSpec(id HealthCheckID) *HealthCheckSpec
HealthCheckSpec returns the HealthCheckSpec for the specified HealthCheckID
errors:
- ErrHealthCheckNotRegistered
- ErrServiceNotAlive
func (AppHealthChecks) PauseHealthCheck ¶
func (a AppHealthChecks) PauseHealthCheck(id HealthCheckID) bool
PauseHealthCheck will kill the healthcheck goroutine. false is returned if no HealthCheck is registered for the specified HealthCheckID
func (AppHealthChecks) PausedHealthChecks ¶
func (a AppHealthChecks) PausedHealthChecks() []HealthCheckID
PausedHealthChecks returns HealthCheckID(s) for registered healthchecks that are not currently scheduled to run, i.e., not alive.
func (AppHealthChecks) Register ¶
func (a AppHealthChecks) Register(id HealthCheckID, healthCheckFunc HealthCheck) error
Registers the HealthCheck for the specified HealthCheckID. If a HealthCheck is already registered for the specified HealthCheckID, then it will be replaced. The bool result indicates if there was a pre-existing healthcheck that was replaced. If true is returned, then it indicates that a healthcheck was replaced, i.e., there was a pre-existing healthcheck registered.
Design notes:
- each healthcheck is scheduled to run on its own scheduled based on its HealthCheckSpec.RunInterval. The scheduling is performed by a scheduling goroutine.
- the healthcheck is run within the command server goroutine, which owns the healtchecks, i.e., the command server goroutine ensures that healthchecks are run serially and in a concurrency safe manner.
errors
- ErrHealthCheckNil
- ErrServiceNotAlive
func (AppHealthChecks) Registered ¶
func (a AppHealthChecks) Registered(id HealthCheckID) bool
Registered returns true if a HealthCheck is registered for the specified HealthCheckID
errors:
- ErrServiceNotAlive
func (AppHealthChecks) ResumeHealthCheck ¶
func (a AppHealthChecks) ResumeHealthCheck(id HealthCheckID) error
ResumeHealthCheck will schedule the healthcheck to run, if it is currently pause.
errors
- ErrHealthCheckNotRegistered
- ErrServiceNotAlive
- ErrAppNotAlive
- HealthCheckKillTimeoutError
func (AppHealthChecks) Run ¶
func (a AppHealthChecks) Run(id HealthCheckID) (HealthCheckResult, error)
Run triggers the healthcheck to run on demand.
errors
- ErrServiceNotAlive
- ErrHealthCheckNotRegistered
type AppMetricRegistry ¶
type AppMetricRegistry struct{}
func (AppMetricRegistry) Counter ¶
func (a AppMetricRegistry) Counter(serviceId ServiceID, metricID MetricID) *CounterMetric
Counter looks up the registered metric.
func (AppMetricRegistry) CounterMetricIds ¶
func (a AppMetricRegistry) CounterMetricIds(serviceId ServiceID) []MetricID
CounterMetricIds returns the registered metric ids for the specified service id
func (AppMetricRegistry) CounterMetricsIdsPerService ¶
func (a AppMetricRegistry) CounterMetricsIdsPerService() map[ServiceID][]MetricID
CounterMetricsIdsPerService returns all counter metric ids grouped by service
func (AppMetricRegistry) CounterVector ¶
func (a AppMetricRegistry) CounterVector(serviceId ServiceID, metricID MetricID) *CounterVectorMetric
CounterVec looks up the registered metric.
func (AppMetricRegistry) CounterVectorMetricIds ¶
func (a AppMetricRegistry) CounterVectorMetricIds(serviceId ServiceID) []MetricID
CounterVectorMetricIds returns the registered metric ids for the specified service id
func (AppMetricRegistry) CounterVectorMetricsIdsPerService ¶
func (a AppMetricRegistry) CounterVectorMetricsIdsPerService() map[ServiceID][]MetricID
CounterMetricsIdsPerService returns all counter metric ids grouped by service
func (AppMetricRegistry) Gauge ¶
func (a AppMetricRegistry) Gauge(serviceId ServiceID, metricID MetricID) *GaugeMetric
Gauge looks up the registered metric.
func (AppMetricRegistry) GaugeMetricIds ¶
func (a AppMetricRegistry) GaugeMetricIds(serviceId ServiceID) []MetricID
GaugeMetricIds returns all gauge metric ids registered for the service
func (AppMetricRegistry) GaugeMetricsByService ¶
func (a AppMetricRegistry) GaugeMetricsByService() map[ServiceID][]MetricID
GaugeMetricsByService returns all gauge metric ids grouped by service
func (AppMetricRegistry) GaugeVector ¶
func (a AppMetricRegistry) GaugeVector(serviceId ServiceID, metricID MetricID) *GaugeVectorMetric
GaugeVector looks up the registered metric.
func (AppMetricRegistry) GaugeVectorMetricIds ¶
func (a AppMetricRegistry) GaugeVectorMetricIds(serviceId ServiceID) []MetricID
GaugeVectorMetricIds returns all gauge vector metric ids registered for the service
func (AppMetricRegistry) GaugeVectorMetricsByService ¶
func (a AppMetricRegistry) GaugeVectorMetricsByService() map[ServiceID][]MetricID
GaugeVectorMetricsByService returns all gauge vector metric ids grouped by service
func (AppMetricRegistry) Histogram ¶
func (a AppMetricRegistry) Histogram(serviceId ServiceID, metricID MetricID) *HistogramMetric
Histogram looks up a registered HistogramMetric
func (AppMetricRegistry) HistogramMetricIds ¶
func (a AppMetricRegistry) HistogramMetricIds(serviceId ServiceID) []MetricID
HistogramMetricIds returns all histogram metric ids for the specified service
func (AppMetricRegistry) HistogramMetricsByService ¶
func (a AppMetricRegistry) HistogramMetricsByService() map[ServiceID][]MetricID
HistogramMetricsByService returns all gauge metric ids grouped by service
func (AppMetricRegistry) HistogramVector ¶
func (a AppMetricRegistry) HistogramVector(serviceId ServiceID, metricID MetricID) *HistogramVectorMetric
HistogramVector looks up a registered HistogramVectorMetric
func (AppMetricRegistry) HistogramVectorMetricIds ¶
func (a AppMetricRegistry) HistogramVectorMetricIds(serviceId ServiceID) []MetricID
HistogramVectorMetricIds returns all histogram vector metric ids for the specified service
func (AppMetricRegistry) HistogramVectorMetricsByService ¶
func (a AppMetricRegistry) HistogramVectorMetricsByService() map[ServiceID][]MetricID
HistogramVectorMetricsByService returns all gauge vector metric ids grouped by service
type AppServices ¶
type AppServices struct{}
func (AppServices) LogLevel ¶
func (a AppServices) LogLevel(id ServiceID) zerolog.Level
LogLevel returns the service log level. The service log level will use the application log level unless it is overridden via the -service-log-level command line flag
func (AppServices) Register ¶
func (a AppServices) Register(s *Service)
Register will register the service with the app.
errors:
- ErrAppNotAlive
- ErrServiceAlreadyRegistered
func (AppServices) Service ¶
func (a AppServices) Service(id ServiceID) *Service
Service will lookup the service for the specified ServiceID
func (AppServices) ServiceIDs ¶
func (a AppServices) ServiceIDs() []ServiceID
ServiceIDs returns the ServiceID(s) for the currently registered services
errors:
- ErrAppNotAlive
func (AppServices) Services ¶
func (a AppServices) Services() []*Service
Services returns all currently registered services
func (AppServices) Unregister ¶
func (a AppServices) Unregister(id ServiceID)
Unregister will unregister the service for the specified ServiceID
errors:
- ErrAppNotAlive
- ErrServiceNotRegistered
type CommandServer ¶
type CommandServer struct { *ServiceCommandChannel // OPTIONAL - used for logging purposes Name string // OPTIONAL - invoked before any commands are run Init func() error // OPTIONAL - invoked when the service has been killed Destroy func() }
CommandServer will execute commands in a background goroutine serially. Think of the command server as a command event loop. The CommandServer can be supplied with optional Init and Destroy funcs.
func NewCommandServer ¶
func NewCommandServer(service *Service, chanSize uint16, name string, init func() error, destroy func()) (*CommandServer, error)
NewCommandServer creates a new CommandServer and returns it
type CounterMetric ¶
type CounterMetric struct { *CounterMetricSpec prometheus.Counter }
CounterMetric associates Counter with its spec. This is cached after the metric is registered.
type CounterMetricSpec ¶
type CounterMetricSpec MetricSpec
CounterMetricSpec type alias for a Counter MetricSpec
func NewCounterMetricSpec ¶
func NewCounterMetricSpec(spec config.CounterMetricSpec) (CounterMetricSpec, error)
NewCounterMetricSpec is a CounterMetricSpec factory method
errors - ConfigError
func (*CounterMetricSpec) CounterOpts ¶
func (a *CounterMetricSpec) CounterOpts() prometheus.CounterOpts
CounterOpts maps the spec to a prometheus CounterOpts
type CounterVectorMetric ¶
type CounterVectorMetric struct { *CounterVectorMetricSpec *prometheus.CounterVec }
CounterVectorMetric associates the CounterVec with its spec. This is cached after the metric is registered.
type CounterVectorMetricSpec ¶
type CounterVectorMetricSpec MetricVectorSpec
CounterVectorMetricSpec is a type alias for a Counter MetricVectorSpec
func NewCounterVectorMetricSpec ¶
func NewCounterVectorMetricSpec(spec config.CounterVectorMetricSpec) (*CounterVectorMetricSpec, error)
NewCounterVectorMetricSpec is the CounterVectorMetricSpec factory method
func (*CounterVectorMetricSpec) CounterOpts ¶
func (a *CounterVectorMetricSpec) CounterOpts() prometheus.CounterOpts
type ErrSpec ¶
type ErrSpec struct { ErrorID ErrorType ErrorSeverity }
type Error ¶
type Error struct { Cause error // user friendly message Message string // Used to identify the exact error type ErrorID ErrorType ErrorSeverity Stack string // tags can be used to categorize errors, e.g., UI, DB, ES, SECURITY Tags []string // should support JSON marshalling - it will be stored as a JSON clob Context interface{} // used for error tracking, i.e., used to lookup this specific error uid.UIDHash time.Time DomainID AppID InstanceID ReleaseID // PID and Hostname are part of the error because errors need to be centrally reported PID int Hostname string ServiceID }
Error is used to model all application errors purposely.
Many developers make the mistake of error propagation as secondary to the flow of their system. Careful consideration is given to how data flows through the system, but errors are something that are tolerated and ferried up the stack without much thought, and ultimately dumped in front of the user. With just a little forethought, and minimal overhead, you can make your error handling an asset to your system.
Errors indicate that your system has entered a state in which it cannot fulfill an operation that a user explicitly or implicitly requested. because of this, it needs to relay a few pieces of critical information:
- What happened
- When and where it happened
Even though the Error fields are directly exposed, Error should be treated as immutable - i.e., once created it should not be changed.
TODO: create capnp message
func AppNotAliveError ¶
func AppNotAliveError() *Error
func HealthCheckAlreadyRegisteredError ¶
func HealthCheckAlreadyRegisteredError(healthCheckID HealthCheckID) *Error
func HealthCheckKillTimeoutError ¶
func HealthCheckKillTimeoutError(healthCheckID HealthCheckID) *Error
func HealthCheckNotAliveError ¶
func HealthCheckNotAliveError(healthCheckID HealthCheckID) *Error
func HealthCheckNotRegisteredError ¶
func HealthCheckNotRegisteredError(healthCheckID HealthCheckID) *Error
func HealthCheckTimeoutError ¶
func HealthCheckTimeoutError(healthCheckID HealthCheckID) *Error
func IllegalArgumentError ¶
func InvalidLogLevelError ¶
func ServiceInitError ¶
func ServiceNotAliveError ¶
func ServiceShutdownError ¶
type ErrorSeverity ¶
type ErrorSeverity uint8
ErrorSeverity is used to classify error severity levels
func (ErrorSeverity) UInt8 ¶
func (a ErrorSeverity) UInt8() uint8
type GaugeMetric ¶
type GaugeMetric struct { *GaugeMetricSpec prometheus.Gauge }
GaugeMetric associates the Gauge with its spec. It is cached after it is registered with prometheus.
type GaugeMetricSpec ¶
type GaugeMetricSpec MetricSpec
GaugeMetricSpec is a type alias for a Gauge MetricSpec
func NewGaugeMetricSpec ¶
func NewGaugeMetricSpec(spec config.GaugeMetricSpec) (GaugeMetricSpec, error)
func (*GaugeMetricSpec) GaugeOpts ¶
func (a *GaugeMetricSpec) GaugeOpts() prometheus.GaugeOpts
GaugeOpts maps the spec to a prometheus GaugeOpts
type GaugeVectorMetric ¶
type GaugeVectorMetric struct { *GaugeVectorMetricSpec *prometheus.GaugeVec }
GaugeVectorMetric associates the Gauge with its spec. It is cached after it is registered with prometheus.
type GaugeVectorMetricSpec ¶
type GaugeVectorMetricSpec MetricVectorSpec
GaugeVectorMetricSpec is a type alaias for the Gauge MetricVectorSpec
func NewGaugeVectorMetricSpec ¶
func NewGaugeVectorMetricSpec(spec config.GaugeVectorMetricSpec) (*GaugeVectorMetricSpec, error)
NewGaugeVectorMetricSpec is the GaugeVectorMetricSpec factory method
errors: - ConfigError
func (*GaugeVectorMetricSpec) GaugeOpts ¶
func (a *GaugeVectorMetricSpec) GaugeOpts() prometheus.GaugeOpts
GaugeOpts maps the spec to a prometheus GaugeOpts
type HealthCheck ¶
type HealthCheck func(result chan<- error, cancel <-chan struct{})
HealthCheck is used to run the health check. params:
- result : the healthcheck will close the channel to signal success. If the healthcheck fails, then an error is sent on the channel.
- cancel : when the cancel channel is closed, then it signals to the healthcheck that it has timed out.
type HealthCheckID ¶
type HealthCheckID uint64
HealthCheckID unique healthcheck id
func (HealthCheckID) Hex ¶
func (a HealthCheckID) Hex() string
type HealthCheckResult ¶
type HealthCheckResult struct { // why the health check failed Err error // when the health check started running time.Time // how long it took to run the health check time.Duration // how many times the health check has failed consecutively ErrCount uint }
HealthCheckResult is the result of running the health check
type HealthCheckSpec ¶
type HealthCheckSpec struct { HealthCheckID RunInterval time.Duration Timeout time.Duration }
HealthCheckSpec is used to configure the healthchecks. Configuration that is loaded will override any runtime configuration. Consider runtime settings specified in the code as the default settings.
All healthchecks are recorded under a GaugeVector (HEALTHCHECK_METRIC_ID). The healthcheck id is used as the healthcheck label value.
The health gauge value indicates the number of consecutive failures. Thus, a gauge value of zero means the last time the health check ran, it succeeded. A gauge value of 3 means the healthcheck has failed the last 3 times it ran. A gauge value of -1 means the healthcheck has not yet been run.
The health check is run via the specified interval and the latest health check result is kept. When metrics are collected, the latest health result will be used to report the metric. It is the application's responsibility to register HealthCheck functions.
The health check run duration is also recorded as a gauge metric (HEALTHCHECK_RUN_DURATION_METRIC_ID). This can help identify health checks that are taking too long to execute. The run duration may also be used to configure alerts. For example, health checks that are passing but taking longer to run may be an early warning sign.
type HistogramMetric ¶
type HistogramMetric struct { *HistogramMetricSpec prometheus.Histogram }
HistogramMetric associates a Histogram with its spetric spec
type HistogramMetricSpec ¶
type HistogramMetricSpec struct { MetricSpec Buckets []float64 // required }
HistogramMetricSpec is a MetricSpec for a Histogram.
func NewHistogramMetricSpec ¶
func NewHistogramMetricSpec(spec config.HistogramMetricSpec) (HistogramMetricSpec, error)
NewHistogramMetricSpec is the HistogramMetricSpec factory method
func (*HistogramMetricSpec) HistogramOpts ¶
func (a *HistogramMetricSpec) HistogramOpts() prometheus.HistogramOpts
HistogramOpts maps the spec to a prometheus HistogramOpts
type HistogramVectorMetric ¶
type HistogramVectorMetric struct { *HistogramVectorMetricSpec *prometheus.HistogramVec }
HistogramVectorMetric associates a HistogramVec with its spetric spec
type HistogramVectorMetricSpec ¶
type HistogramVectorMetricSpec struct { *MetricVectorSpec Buckets []float64 }
HistogramVectorMetricSpec is a MetricVectorSpec for a histogram vector
func NewHistogramVectorMetricSpec ¶
func NewHistogramVectorMetricSpec(spec config.HistogramVectorMetricSpec) (*HistogramVectorMetricSpec, error)
NewHistogramVectorMetricSpec is the HistogramVectorMetricSpec factory method
func (*HistogramVectorMetricSpec) HistogramOpts ¶
func (a *HistogramVectorMetricSpec) HistogramOpts() prometheus.HistogramOpts
HistogramOpts maps the spec to a prometheus HistogramOpts
type InstanceID ¶
type InstanceID uint64
InstanceID is the unique id for an app instance. There may be multiple instances, i.e., processes, of an app running. The instance id is used to differentiate the different app instances. For examples, logs and metrics will contain the instance id.
func Instance ¶
func Instance() InstanceID
InstanceID returns a new unique instance id each time the app is started, i.e., when the process is started. The instance id remains for the lifetime of the process
func (InstanceID) Hex ¶
func (a InstanceID) Hex() string
func (InstanceID) UInt64 ¶
func (a InstanceID) UInt64() uint64
type LogEventID ¶
type LogEventID uint64
LogEventID
func (LogEventID) Hex ¶
func (a LogEventID) Hex() string
type MetricID ¶
type MetricID uint64
MetricID unique error id
func (MetricID) PrometheusName ¶
Name returns the metric formatted name for Prometheus. The name must match the regex : [a-zA-Z_:][a-zA-Z0-9_:]*
type MetricSpec ¶
MetricSpec specifies a service metric. Metrics are scoped to services.
Metric naming convention : op_{ServiceID.Hex()}_{MetricID.Hex()}
op_e49214fa20b35ba8_db722ec1bb1b5766
where op stands for OysterPack
Think of ServiceID and MetricID together as the unique key for a Service metric.
Each metric will have the following constant labels
- domain : DomainID.Hex()
- app : AppID.Hex
- svc : ServiceID.Hex()
type MetricVectorSpec ¶
type MetricVectorSpec struct { MetricSpec DynamicLabels []string }
MetricVectorSpec specifies a service metric vector. The difference between a simple metric and a metric vector is that a metric vector can have dimensions, which are defined by dynamic labels. The labels are called dynamic because the the label value is not constant.
When a metric is recorded for a metric vector, all label values must be specified.
type Service ¶
type Service struct { tomb.Tomb // contains filtered or unexported fields }
Service represents an application service. The service lifecycle is controlled by its Tomb.
func NewService ¶
NewService is the service factory method. ServiceID must not be zero, i.e., a zero ServiceID will trigger a panic.
type ServiceCommandChannel ¶
type ServiceCommandChannel struct { *Service // contains filtered or unexported fields }
ServiceCommandChannel pairs a command channel to a service. It is the embedding service's responsibility to subscribe to service command channel and execute the command functions.
Service Channel Server Design Pattern
The server's main goroutine will listen on the command channel, and when a command function is received it will execute the function.
The service state is accessed within the service's main goroutine, i.e., the code executed by the command can assume that it will be run in a threadsafe manner.
Data is passed into the service via the function closure.
Data is passed out over channels that are enclosed by the closure function.
The service's main goroutine acts like an event loop which executes code that is received on the service commmand channel
NOTE: the service may embed more than 1 service command channel.
example service main goroutine :
a.Go(func() error { for { // command event loop select { case <- a.Dying(): // listen for kill signal a.stop() // stop the service gracefully return nil // server exit case f := <- a.CommandChan(): // listen for commands f() // execute the command } } }
CommandServer is reference implementation and is designed to be embedded into services.
func NewServiceCommandChannel ¶
func NewServiceCommandChannel(service *Service, chanSize uint16) (*ServiceCommandChannel, error)
NewServiceCommandChannel creates a new ServiceCommandChannel with the specified channel buffer size.
func (*ServiceCommandChannel) CommandChan ¶
func (a *ServiceCommandChannel) CommandChan() <-chan func()
CommandChan returns the chan to used to receive commands to execute.
func (*ServiceCommandChannel) Submit ¶
func (a *ServiceCommandChannel) Submit(f func()) error
Submit will put the command function on the service channel. Submit will block until the command function can be put on the channel.
errors:
- ErrServiceNotAlive
type ServiceErrSpec ¶
Source Files ¶
- app.go
- config.go
- doc.go
- errors.go
- healthcheck_registry.go
- healthchecks.go
- ids.go
- log_events.go
- metric_counter_registry.go
- metric_counters.go
- metric_gauge_registry.go
- metric_gauges.go
- metric_histogram_registry.go
- metric_histograms.go
- metrics.go
- metrics_http_reporter.go
- service.go
- service_command_channel.go
- test_support.go
- utils.go