Git Product home page Git Product logo

health's Introduction

Kitura

APIDoc Build Status - Master codecov macOS Linux Apache 2 Slack Status

Health

The Health package provides a basic infrastructure that Swift applications can use for reporting their overall health status.

As an application developer, you create an instance of the Health class and then register one or more health checks. A health check can be either a closure that conforms to the HealthCheckClosure typealias or a class that conforms to the HealthCheck protocol. Once you have your health checks in place, you can ask your Health instance for its status.

Swift version

The latest version of Health works with the 4.1.2 version of the Swift binaries. You can download this version of the Swift binaries by following this link.

Usage

Add dependencies

Add Health to the dependencies within your application's Package.swift file. Substitute "x.x.x" with the latest Health release.

.package(url: "https://github.com/Kitura/Health.git", from: "x.x.x")

Add Health to your target's dependencies:

.target(name: "example", dependencies: ["Health"]),

Initialize Health

The example code below shows how to create a Health instance and register your health checks:

import Health

let health = Health()

// Add custom checks
health.addCheck(check: MyCheck1())
health.addCheck(check: MyCheck2())
health.addCheck(check: myClosureCheck1)
health.addCheck(check: myClosureCheck1)

// Get current health status
let count = health.numberOfChecks
let status: Status = health.status
let state: State = status.state
let dictionary = status.toDictionary()
let simpleDictionary = status.toSimpleDictionary()

The simple dictionary contains a key-value pair that lets you know whether the application is UP or DOWN:

["status": "UP"]

The dictionary contains a key-value pair that lets you know whether the application is UP or DOWN, additional details about the health checks that failed (if any), and a Universal Time Coordinated (UTC) timestamp value:

["status": "DOWN", "timestamp": "2017-06-12T18:04:38+0000", "details": ["Cloudant health check.", "A health check closure reported status as DOWN."]]

Swift applications can use either dictionary, depending on the use case, to report the overall status of the application. For instance, an endpoint on the application could be defined that queries the Health object to get the overall status and then send it back to a client as a JSON payload.

The Status structure now conforms to the Codable protocol, which enables you to serialize an instance of this structure and send it as a response to a client. If you use this mechanism you don't need to invoke either the toDictionary() or the toSimpleDictionary() methods in order to obtain the status payload for a client:

let status: Status = health.status
let payload = try JSONEncoder().encode(status)
// Send payload to client

Caching

When you create an instance of the Health class, you can pass an optional argument (named statusExpirationTime) to its initializer as shown next:

let health = Health(statusExpirationTime: 30000)

The statusExpirationTime parameter specifies the number of milliseconds that a given instance of Health should cache its status for before recomputing it. For instance, if the value assigned to statusExpirationTime is 30000 (as shown above), then 30 seconds must elapse before the Health instance computes its status again by querying each health check that has been registered. The default value for the statusExpirationTime parameter is 30000.

Implementing a health check

You can implement health checks by either extending the HealthCheck protocol or creating a closure that returns a State value.

The following snippet of code shows the implementation of a class named MyCustomCheck, which implements the HealthCheck protocol:

class MyCustomCheck: HealthCheck {
  public var name: String { get { return "MyCustomCheck for XYZ"} }

  public var description: String { get { return "Description for MyCustomCheck..."} }

  public func evaluate() -> State {
    let state: State = isConnected() ? State.UP : State.DOWN
    return state
  }

  private func isConnected() -> Bool {
    ...
  }
}

The following snippet of code shows the implementation for a similar health check but using a closure instead:

func myCustomCheck() -> State {
  let state: State = isConnected() ? State.UP : State.DOWN
  return state
}

func isConnected() -> Bool {
  ...
}

Using Health in a Kitura application

One common use case for this Swift package is to integrate it into a Kitura-based application, as shown below:

import Kitura
import Foundation

...

// Create main objects...
router = Router()

health = Health()

// Register health checks...
health.addCheck(check: Microservice1Check())
health.addCheck(check: microservice2Check)

...

// Define /health endpoint that leverages Health
router.get("/health") { request, response, next in
  // let status = health.status.toDictionary()
  let status = health.status.toSimpleDictionary()
  if health.status.state == .UP {
    try response.send(json: status).end()
  } else {
    try response.status(.serviceUnavailable).send(json: status).end()
  }
}

In the code sample above, the health of the application is exposed through the /health endpoint. Cloud environments (e.g. Cloud Foundry, Kubernetes, etc.) can then use the status information returned from the /health endpoint to monitor and manage the Swift application instance.

As an alternative to the implementation shown above for the /health endpoint, you can take advantage of the Codable protocol available in Swift 4. Since the Status struct satisfies the Codable protocol, a simpler implementation for the /health endpoint can be implemented using the new codable capabilities in Kitura 2.0 as shown below:

...

// Define /health endpoint that leverages Health
router.get("/health") { request, response, next in
  let status = health.status
  if health.status.state == .UP {
    try response.status(.OK).send(status).end()
  } else {
    try response.status(.serviceUnavailable).send(status).end()
  }
}

...

In addition to sending the dictionary response, a server needs to respond with a non-200 status code, if the health state is considered down. This can be accomplished with a status code such as 503 .serviceUnavailable. That way, the cloud environment can recognize the negative health response, destroy that instance of the application, and restart the application.

Using Cloud Foundry Environment

If using a Cloud Foundry environment, make sure to update your manifest.yml to support health check. In the example above, you would set the health-check-type value to http and the health-check-http-endpoint to the correct health endpoint path, which is /health in this case. Review the health check documentation for more details.

API documentation

For more information visit our API reference.

Community

We love to talk server-side Swift and Kitura. Join our Slack to meet the team!

License

This library is licensed under Apache 2.0. Full license text is available in LICENSE.

health's People

Contributors

dannys42 avatar djones6 avatar enriquel8 avatar helenmasters avatar ianpartridge avatar kweinmeister avatar mamabusi avatar rolivieri avatar shihabmehboob avatar swiftdevops avatar tfrank64 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

health's Issues

EXC_BAD_INSTRUCTION in status-getter because of negative UInt64

Hi,
while accessing the status-getter of Health a EXC_BAD_INSTRUCTION crash is possible, because the line Date.currentTimeMillis() - self.lastStatus.tsInMillis in Health.swift
It is possible that self.lastStatus.tsInMillis is > Date.currentTimeMillis(), which would create a negative number.

Why is it possible that self.lastStatus.tsInMillis > Date.currentTimeMillis():
The rounding behaviour from DateFormatter.string(from date: Date) (which is used to create the timestamp of Status) and UInt64(date.timeIntervalSince1970 * 1000.0) (which is used in currentTimeMillis()) is different.

Example:

let date1 = Date()
print(date1.timeIntervalSince1970) // 1529757278.70769
let date1String = StatusDateFormatter().string(from: date1)
print(Status.dateFormatter.date(from: date1String)!.milliseconds) // 1529757278708

let date2 = Date()
print(date2.timeIntervalSince1970) // 1529757278.70794
print(UInt64(date2.timeIntervalSince1970 * 1000.0)) // 1529757278707

You can easily create a loop that runs this code after < 1second the milliseconds of date1 are larger than of date2.

Because it is is very common that health-checks run very often and from multiple sources, it is very likely that after a short time such situation happen. (In our situation every < 12h a server is crashing because of this)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.