View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        jan@swi-prolog.org
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (C): 2024 SWI-Prolog Solutions b.v.
    7
    8    This program is free software; you can redistribute it and/or
    9    modify it under the terms of the GNU General Public License
   10    as published by the Free Software Foundation; either version 2
   11    of the License, or (at your option) any later version.
   12
   13    This program is distributed in the hope that it will be useful,
   14    but WITHOUT ANY WARRANTY; without even the implied warranty of
   15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16    GNU General Public License for more details.
   17
   18    You should have received a copy of the GNU General Public
   19    License along with this library; if not, write to the Free Software
   20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   21
   22    As a special exception, if you link this library with other files,
   23    compiled with a Free Software compiler, to produce an executable, this
   24    library does not by itself cause the resulting executable to be covered
   25    by the GNU General Public License. This exception does not however
   26    invalidate any other reasons why the executable file might be covered by
   27    the GNU General Public License.
   28*/
   29
   30:- module(http_server_health,
   31          [ server_health/1             % +Request
   32          ]).   33:- autoload(library(lists), [member/2]).   34:- autoload(library(aggregate), [aggregate_all/3, aggregate_all/4]).   35:- autoload(library(apply), [maplist/3]).   36:- autoload(library(option), [option/2]).   37:- autoload(library(http/http_dispatch), [http_handler/3]).   38:- autoload(library(http/http_json), [reply_json/1]).   39:- autoload(library(http/http_parameters), [http_parameters/2]).   40:- autoload(library(http/thread_httpd), [http_server_property/2, http_workers/2]).   41:- autoload(library(http/http_stream), [cgi_statistics/1]).   42:- use_module(library(http/http_cors), [cors_enable/2, cors_enable/0]).   43
   44:- http_handler(root(health), server_health, [id(server_health), priority(-10)]).

HTTP Server health statistics

This module defines an HTTP handler for /health. The handler returns a JSON document with elementary health statistics on the running instance. The location can be changed using http_handler/3. Keys may be added using additional clauses for health/2 or hidden using hide/1.

This library defines an HTTP handler and defines two multifile predicates (health/2 and hide/1) to control the information presented. */

 server_health(+Request)
HTTP handler that replies with the overall health of the server. Returns a JSON object from all solutions of health/2.

Processes an optional parameter fields to specify the fields that should be returned. The fields content is "," or white space delimited.

   66server_health(Request) :-
   67    option(method(options), Request), !,
   68    cors_enable(Request,
   69                [ methods([get])
   70                ]),
   71    format('~n').
   72server_health(Request) :-
   73    http_parameters(Request,
   74                    [ fields(FieldSpec, [ optional(true) ])
   75                    ]),
   76    cors_enable,
   77    (   var(FieldSpec)
   78    ->  true
   79    ;   split_string(FieldSpec, ", \t\r\n", " \t\r\n", Strings),
   80        maplist(atom_string, Fields, Strings)
   81    ),
   82    get_server_health(Health, Fields),
   83    reply_json(Health).
   84
   85get_server_health(Health, Fields) :-
   86    var(Fields),
   87    !,
   88    findall(Key-Value, health(Key, Value), Pairs),
   89    dict_pairs(Health, health, Pairs).
   90get_server_health(Health, Fields) :-
   91    findall(Key-Value, (member(Key,Fields),health(Key, Value)), Pairs),
   92    dict_pairs(Health, health, Pairs).
 health(-Key, -Value) is nondet
Multifile extensible. True when Key/Value can be reported as a health statistics. Keys may be added by adding clauses to this multifile predicate. Keys may be filtered using hide/1. Predefined Key values are:
up
Defined to be true.
epoch
Starting time of the server in seconds after Jan 1, 1970 UTC.
cpu_time
Total process CPU usage in seconds.
threads
Number of active threads
workers
Number of HTTP worker threads.
requests
Number of HTTP requests processed.
bytes_sent
Number of bytes send in reply to HTTP requests.
open_files
Number of open file streams. This includes physical files as well as sockets (except for Windows). On Linux we count the file handles in /proc/self/fd. Otherwise we use stream_property/2 with the file_no(Fd) property.
loadavg
An array holding the load average over the last [1,5,15] minutes. This key is only supported on Linux machines. It is based on /proc/loadavg
heap
When compiled with TCMalloc, this provides two properties:
inuse:Bytes
Total amount of in-use memory in bytes
size:Bytes
Same as inuse, but including the TCMalloc overhead and (thus) memory that has been freed and is not (yet) reused.
Arguments:
Key- is the name of the JSON key. Must be an atom
Value- is the Prolog representation for a JSON (dict) value.
  135:- multifile health/2.  136
  137term_expansion((health(Key,Value) :- Body),
  138               (health(Key,Value) :- \+ hide(Key), Body)).
  139
  140health(up, true) :-
  141    true.
  142health(epoch, Epoch) :-
  143    http_server_property(_, start_time(Epoch)).
  144health(cpu_time, Time) :-
  145    statistics(process_cputime, Time).
  146health(threads, Count) :-
  147    statistics(threads, Count).
  148health(workers, Count) :-
  149    aggregate_all(sum(N), http_workers(_,N), Count).
  150health(requests, RequestCount) :-
  151    cgi_statistics(requests(RequestCount)).
  152health(bytes_sent, BytesSent) :-
  153    cgi_statistics(bytes_sent(BytesSent)).
  154:- if(exists_directory('/proc/self/fd')).  155health(open_files, Streams) :-
  156    directory_files('/proc/self/fd', FDs),
  157    length(FDs, Files),
  158    Streams is Files - 2.               % Ignore . and ..
  159:- else.  160health(open_files, Streams) :-
  161    findall(N, stream_property(_, file_no(N)), FDs),
  162    sort(FDs, Unique),
  163    length(Unique, Streams).
  164:- endif.  165health(loadavg, LoadAVG) :-
  166    access_file('/proc/loadavg', exist),
  167    catch(setup_call_cleanup(
  168              open('/proc/loadavg', read, In),
  169              read_string(In, _, String),
  170              close(In)),
  171	      _, fail),
  172    split_string(String, " ", " ", [One,Five,Fifteen|_]),
  173    maplist(number_string, LoadAVG, [One,Five,Fifteen]).
  174:- if(current_predicate(malloc_property/1)).  175health(heap, json{inuse:InUse, size:Size}) :-
  176    malloc_property('generic.current_allocated_bytes'(InUse)),
  177    malloc_property('generic.heap_size'(Size)).
  178:- endif.
 hide(?Key) is nondet
Multifile hook. If true for a specific Key, hide this statistics from the output. This may be used to hide keys that are considered a security risk.
  186:- multifile hide/1.