errawr-gen

Standardized authoritative error management and structure

APACHE-2.0 License

Stars
3
Committers
3

errawr-gen

errawr-gen generates standard error constructors for errawr-compatible errors. It reads a YAML file of errors and outputs code for a specified language. Currently only Go is supported.

Usage of errawr-gen:
  -input-path string
      the path to read input from (default "-")
  -output-language string
      the language to write errors for (default "go")
  -output-path string
      the path to write output to (default "-")
  -package string
      the package to write

Structure

Domains

Error codes are constructed by combining a domain, a section, and an error. Domains represent a unique project or namespace. They are identified by a short abbreviation. For example:

In general, error messages from complete applications should use an obvious four-letter abbreviation. Other libraries and tools should can use a longer abbreviation if necessary. (In the example above, hsch is "Horsehead scheduler".)

Reserved domains

The following domains are reserved:

Name Description
err Wrapper for errors generated by sources external to errawr

Sections

A domain is divided into sections. A section represents a logically distinct portion of the domain. Each domain will have its own rules and best practices for defining sections, and some may have none at all.

For example, the insights-dataflow/controller domain might define the sections model and server. Sections should usually be short identifiers constructed using snake case.

Errors

Each section contains pertinent errors. The input file to the generator defines the mapping of a domain to sections and sections to errors. For example:

version: 1
domain:
  key: lsq
  title: SQL query generation library
sections:
  driver:
    title: Driver errors
    errors:
      tcp_connection_error:
        title: TCP connection error
        description:
          friendly: >
            We could not access this service. You may need to check your
            firewall configuration.
          technical: >
            The host {{code host}} is not connectable on TCP port {{port}}.
        arguments:
          host:
            description: the host name
          port:
            type: integer
            validators:
              - positive_number
              - integer
            description: the TCP port number
      authentication_error:
        title: Authentication error
        description: >
          We could not authenticate to this service with the credentials
          provided.

All errors must have, at minimum, a title and a description.

Descriptions

Frequently, descriptions are just text strings.

A description may optionally be an object with one or more of the following keys: friendly, technical. Descriptions are Handlebars templates and substitute variables from the arguments mapping. Additionally, several formatting helpers are available, roughly following the style of HTML:

Helper HTML equivalent Text equivalent
{{em text}} <em>{{text}}</em> *{{text}}*
{{link url text}} <a href="{{url}}">{{text}}</a> {{text}} ({{url}})
{{#join list}}#{{@index}}{{/join}} <ul><li>#0</li><li>#1</li></ul> #1 and #2
{{pre text}} <code>{{text}}</code> `{{text}}`
{{quote text}} &ldquo;{{text}}&rdquo; "{{text}}"

Arguments

Arguments are mapping of a name (which can be substituted in the description template as described above) to an argument definition. No information is required to be included in an argument definition; mapping entries may simply be empty values.

A description of an argument may be provided, which can be useful for supporting users when trying to understand an error. Generally, the description is not surfaced to end users directly.

A type may be provided. Types mostly map directly to JSON, but are a bit more constrained. Valid types:

  • string
  • number
  • integer
  • boolean
  • list<string>
  • list<number>
  • list<integer>
  • list<boolean>

A default may be provided, which will be substituted in the description template if no value is provided for the argument when the error is created. If this key is not defined, a value for the argument must be supplied when the error is created.

A list of validators may be provided. Validators check that the arguments are sane before allowing them to be surfaced and used in the description templates. If any validator fails, the argument is invalidated, and the default value is used instead. The following validators are available:

Name Description
number Asserts that the argument is a decimal number.
positive_number Like number, but also checks that the argument is greater than 0.
nonnegative_number Like number, but also checks that the argument is at least 0.
integer Asserts that the argument is an integer.

Sensitivity

An error may have environment-specific sensitivity, which allows fine-grained control over whether the details (description, arguments, and causes) of an error are exported to external consumers. Sensitivities are integers that range from 0 (least sensitive) to 1000 (most sensitive). Currently, only four sensitivities are defined:

Name Key Value Description
None none 0 These errors can be presented anywhere, even to third parties.
Edge edge 100 These errors are restricted to components of the same system. Edge-sensitive errors cross error domains, but are not propagated to third-party components.
Bug bug 200 These errors can reasonably be displayed in certain intra-system interfaces, but are more restricted than edge errors.
All all 1000 These errors may only be displayed within the same error domain.

An error's sensitivity can be changed at runtime, but can only be increased. The initial sensitivity for an error can be declared in the YAML definition:

# ...
restricted_error:
  title: A very secret error
  sensitivity: all
  # ...

Representation

An error identifier is the concatenation of the error's domain, section, and code, separated by underscores. In the example above, the error identifier is lsq_driver_tcp_connection_error.

These representations are unique within the entire error collection and can be used by support team members to easily identify a specific error.

Errors

When rendered, an error has the following object structure (note that code contains the error identifier from the end user's perspective):

{
  "domain": "lsq",
  "section": "driver",
  "code": "lsq_driver_tcp_connection_error",
  "title": "TCP connection error",
  "description": {
    "friendly": "We could not access this service. You may need to check your firewall configuration.",
    "technical": "The host {{code host}} is not connectable on TCP port {{port}}."
  },
  "arguments": {
    "host": "localhost",
    "port": "5432"
  },
  "formatted": {
    "friendly": "We could not access this service. You may need to check your firewall configuration.",
    "technical": "The host `localhost` is not connectable on TCP port 5432."
  },
  "causes": []
}

These property semantics should be fairly intuitive. Of note, however, is the formatted property, in which the error renderer has substituted the arguments into the description templates, and the causes property, which contains an optional list of more error objects that caused the given error to occur. If there are no additional causes, the causes property may be omitted altogether.

Style

In general, error descriptions should always be complete sentences. They should always end with a punctuation mark, generally a period.

Use of the passive voice ("the account is deactivated") should be avoided where possible. It may occur in technical descriptions, but should be used sparingly in friendly descriptions as users tend to find it inaccessible. In friendly descriptions, use the pronoun "we" to refer to Puppet as a collective, and the pronoun "you" to refer to the end user when they can take action on their own.

Never use gendered pronouns in any part of error definitions.

Argument descriptions should be simple phrases like "the host name." Complete sentences are not necessary.

Libraries