├── .gitignore ├── .travis.yml ├── CHANGES.rst ├── COPYING ├── LICENSE ├── Makefile.am ├── README.rst ├── autogen.sh ├── configure.ac ├── example.vcl ├── m4 └── PLACEHOLDER ├── readmeAsWell.rst ├── src ├── HTML │ ├── vmod_html.c │ └── vmod_html.h ├── Makefile.am ├── tests │ ├── test01.vtc │ ├── test03.vtc │ └── test04.vtc ├── vmod_rtstatus.c ├── vmod_rtstatus.h ├── vmod_rtstatus.vcc └── vmod_rtstatus_cache.c └── varnishstatus.png /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | Makefile.in 3 | .deps/ 4 | .libs/ 5 | *.o 6 | *.lo 7 | *.la 8 | *~ 9 | *.[1-9] 10 | 11 | /aclocal.m4 12 | /autom4te.cache/ 13 | /compile 14 | /config.guess 15 | /config.h 16 | /config.h.in 17 | /config.log 18 | /config.status 19 | /config.sub 20 | /configure 21 | /depcomp 22 | /install-sh 23 | /libtool 24 | /ltmain.sh 25 | /missing 26 | /stamp-h1 27 | /m4/ 28 | 29 | /src/vcc_if.c 30 | /src/vcc_if.h 31 | 32 | /src/vmod_rtstatus*rst 33 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | before_install: 4 | - sudo apt-get update -q 5 | - sudo apt-get install -qq apt-transport-https python-docutils 6 | - curl -L https://packagecloud.io/varnishcache/varnish40/gpgkey | sudo apt-key add - 7 | - echo "deb https://packagecloud.io/varnishcache/varnish40/ubuntu/ precise main" | sudo tee /etc/apt/sources.list.d/varnish-cache.list 8 | - sudo apt-get -q update 9 | - sudo apt-get install varnish varnish-dev 10 | 11 | before_script: 12 | - ./autogen.sh 13 | - ./configure --prefix=/usr 14 | - make -j4 15 | 16 | script: 17 | - make check -j4 18 | 19 | compiler: 20 | - clang 21 | - gcc 22 | -------------------------------------------------------------------------------- /CHANGES.rst: -------------------------------------------------------------------------------- 1 | libvmod-rtstatus 1.1.1 (unreleased) 2 | ----------------------------------- 3 | 4 | * Version change in configure.ac. 5 | * Debian folder and spec file deleted. 6 | 7 | libvmod-example 1.1.0 (2015-12-09) 8 | ---------------------------------- 9 | 10 | Bugs fixed 11 | ---------- 12 | 13 | - 8_ - Workspace saturation after increasing number of backends. 14 | 15 | .. _8: https://github.com/varnish/libvmod-rtstatus/issues/8 16 | 17 | 18 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Varnish Software AS 2 | ... 3 | See LICENSE for details. 4 | 5 | You're free to use and distribute this under terms in the 6 | LICENSE. Please add your relevant copyright statements. 7 | 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Redistribution and use in source and binary forms, with or without 2 | modification, are permitted provided that the following conditions 3 | are met: 4 | 1. Redistributions of source code must retain the above copyright 5 | notice, this list of conditions and the following disclaimer. 6 | 2. Redistributions in binary form must reproduce the above copyright 7 | notice, this list of conditions and the following disclaimer in the 8 | documentation and/or other materials provided with the distribution. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 11 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 12 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 13 | ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 14 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 15 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 16 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 17 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 18 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 19 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 20 | SUCH DAMAGE. 21 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = -I m4 -I ${LIBVARNISHAPI_DATAROOTDIR}/aclocal 2 | 3 | SUBDIRS = src 4 | 5 | DISTCHECK_CONFIGURE_FLAGS = \ 6 | VMOD_DIR='$${libdir}/varnish/vmods' 7 | 8 | 9 | EXTRA_DIST = CHANGES.rst README.rst LICENSE 10 | 11 | doc_DATA = CHANGES.rst README.rst LICENSE 12 | 13 | dist_man_MANS = vmod_rtstatus.3 14 | MAINTAINERCLEANFILES = $(dist_man_MANS) 15 | 16 | vmod_rtstatus.3: README.rst 17 | 18 | %.1 %.2 %.3 %.4 %.5 %.6 %.7 %.8 %.9: 19 | if HAVE_RST2MAN 20 | ${RST2MAN} $< $@ 21 | else 22 | @echo "========================================" 23 | @echo "You need rst2man installed to make dist" 24 | @echo "========================================" 25 | @false 26 | endif 27 | 28 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | 2 | ============= 3 | vmod_rtstatus 4 | ============= 5 | 6 | .. image:: https://travis-ci.org/varnish/libvmod-rtstatus.svg?branch=4.0 7 | :target: https://travis-ci.org/varnish/libvmod-rtstatus 8 | 9 | ------------------------------- 10 | Varnish Real-Time Status Module 11 | ------------------------------- 12 | 13 | :Author: Arianna Aondio 14 | :Date: 2015-12-09 15 | :Version: 1.1.1 16 | 17 | SYNOPSIS 18 | ======== 19 | 20 | import rtstatus; 21 | 22 | DESCRIPTION 23 | =========== 24 | 25 | A vmod that lets you query your Varnish server for a JSON object containing 26 | counters. 27 | 28 | Visiting the URL ``/rtstatus.json`` on the Varnish server will produce an 29 | application/json response of the following format:: 30 | 31 | { 32 | "Uptime" : 0+00:09:38, 33 | "hitrate": 0.00, 34 | "load": 1, 35 | "varnish_version" : "varnish-4.0.1 revision c6f20e4", 36 | "server_id": "arianna-ThinkPad-X230", 37 | "client_id": "127.0.0.1", 38 | "backend": [{"director_name" : "simple" , "name":"default", "value": "healthy"}, 39 | {"director_name" : "simple" , "name":"server1", "value": "healthy"}, 40 | {"director_name" : "simple" , "name":"server2", "value": "healthy"}], 41 | "MAIN.uptime": {"type": "MAIN", "descr": "Child process uptime", "value": 578}, 42 | "VBE.server1(192.168.0.10,,8081).vcls": {"type": "VBE", "ident": "server1(192.168.0.10,,8081)", "descr": "VCL references", "value": 1}, 43 | "VBE.server1(192.168.0.10,,8081).happy": {"type": "VBE", "ident": "server1(192.168.0.10,,8081)", "descr": "Happy health probes", "value": 0}, 44 | "VBE.server1(192.168.0.10,,8081).bereq_hdrbytes": {"type": "VBE", "ident": "server1(192.168.0.10,,8081)", "descr": "Request header bytes", "value": 0}, 45 | } 46 | 47 | Visiting the URL ``/rtstatus`` on the Varnish server will produce an 48 | application/javascript response of the following format: 49 | 50 | .. image:: varnishstatus.png 51 | :alt: RTstatus frontend 52 | 53 | FUNCTIONS 54 | ========= 55 | 56 | rtstatus 57 | -------- 58 | 59 | Prototype:: 60 | 61 | rtstatus(REAL delta) 62 | *delta* is the interval of seconds used for hitrate and load calculations. 63 | It has to be > 0 and < 60 seconds. 64 | 65 | Return value 66 | STRING 67 | 68 | html() 69 | ------ 70 | 71 | Prototype:: 72 | 73 | html( ) 74 | 75 | Return value 76 | STRING 77 | 78 | INSTALLATION 79 | ============ 80 | The source tree is based on autotools to configure the building, and 81 | does also have the necessary bits in place to do functional unit tests 82 | using the varnishtest tool. 83 | 84 | Make targets: 85 | 86 | * make - builds the vmod 87 | * make install - installs your vmod in `VMODDIR` 88 | * make check - runs the unit tests in ``src/tests/*.vtc`` 89 | 90 | In your VCL you could then use this vmod along the following lines:: 91 | 92 | vcl 4.0; 93 | import std; 94 | import rtstatus; 95 | 96 | sub vcl_recv { 97 | if (req.url ~ "/rtstatus.json") { 98 | return(synth(700, "OK")); } 99 | if (req.url ~ "/rtstatus") { 100 | return(synth(800, "OK")); 101 | } 102 | } 103 | sub vcl_synth { 104 | if (resp.status == 700){ 105 | set resp.status = 200; 106 | set resp.http.Content-Type = "application/json; charset=utf-8"; 107 | synthetic(rtstatus.rtstatus(5)); 108 | return (deliver); 109 | } 110 | if (resp.status == 800) { 111 | set resp.status = 200; 112 | set resp.http.Content-Type = "text/html; charset=utf-8"; 113 | synthetic(rtstatus.html()); 114 | return (deliver); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | warn() { 4 | echo "WARNING: $@" 1>&2 5 | } 6 | 7 | case `uname -s` in 8 | Darwin) 9 | LIBTOOLIZE=glibtoolize 10 | ;; 11 | FreeBSD) 12 | LIBTOOLIZE=libtoolize 13 | ;; 14 | Linux) 15 | LIBTOOLIZE=libtoolize 16 | ;; 17 | SunOS) 18 | LIBTOOLIZE=libtoolize 19 | ;; 20 | *) 21 | warn "unrecognized platform:" `uname -s` 22 | LIBTOOLIZE=libtoolize 23 | esac 24 | 25 | automake_version=`automake --version | tr ' ' '\n' | egrep '^[0-9]\.[0-9a-z.-]+'` 26 | if [ -z "$automake_version" ] ; then 27 | warn "unable to determine automake version" 28 | else 29 | case $automake_version in 30 | 0.*|1.[0-8]|1.[0-8][.-]*) 31 | warn "automake ($automake_version) detected; 1.9 or newer recommended" 32 | ;; 33 | *) 34 | ;; 35 | esac 36 | fi 37 | 38 | # check for varnishapi.m4 in custom paths 39 | dataroot=$(pkg-config --variable=datarootdir varnishapi 2>/dev/null) 40 | if [ -z "$dataroot" ] ; then 41 | cat >&2 <<'EOF' 42 | Package varnishapi was not found in the pkg-config search path. 43 | Perhaps you should add the directory containing `varnishapi.pc' 44 | to the PKG_CONFIG_PATH environment variable 45 | EOF 46 | exit 1 47 | fi 48 | 49 | set -ex 50 | 51 | aclocal -I m4 -I ${dataroot}/aclocal 52 | $LIBTOOLIZE --copy --force 53 | autoheader 54 | automake --add-missing --copy --foreign 55 | autoconf 56 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ(2.59) 2 | AC_COPYRIGHT([Copyright (c) 2015 Varnish Software AS]) 3 | AC_INIT([libvmod-rtstatus], [1.1.1]) 4 | AC_CONFIG_MACRO_DIR([m4]) 5 | AC_CONFIG_SRCDIR(src/vmod_rtstatus.vcc) 6 | AC_CONFIG_HEADERS(config.h) 7 | 8 | AC_CANONICAL_SYSTEM 9 | AC_LANG(C) 10 | 11 | AM_INIT_AUTOMAKE([foreign]) 12 | 13 | AC_GNU_SOURCE 14 | AC_PROG_CC 15 | AC_PROG_CC_STDC 16 | if test "x$ac_cv_prog_cc_c99" = xno; then 17 | AC_MSG_ERROR([Could not find a C99 compatible compiler]) 18 | fi 19 | AC_PROG_CPP 20 | 21 | AC_PROG_INSTALL 22 | AC_PROG_LIBTOOL 23 | AC_PROG_MAKE_SET 24 | 25 | # Check for rst utilities 26 | AC_CHECK_PROGS(RST2MAN, [rst2man rst2man.py], "no") 27 | if test "x$RST2MAN" = "xno"; then 28 | AC_MSG_WARN([rst2man not found - not building man pages]) 29 | fi 30 | AM_CONDITIONAL(HAVE_RST2MAN, [test "x$RST2MAN" != "xno"]) 31 | 32 | # Checks for header files. 33 | AC_HEADER_STDC 34 | AC_CHECK_HEADERS([sys/stdlib.h]) 35 | 36 | # backwards compat with older pkg-config 37 | # - pull in AC_DEFUN from pkg.m4 38 | m4_ifndef([PKG_CHECK_VAR], [ 39 | # PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, 40 | # [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) 41 | # ------------------------------------------- 42 | # Retrieves the value of the pkg-config variable for the given module. 43 | AC_DEFUN([PKG_CHECK_VAR], 44 | [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl 45 | AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl 46 | 47 | _PKG_CONFIG([$1], [variable="][$3]["], [$2]) 48 | AS_VAR_COPY([$1], [pkg_cv_][$1]) 49 | 50 | AS_VAR_IF([$1], [""], [$5], [$4])dnl 51 | ])# PKG_CHECK_VAR 52 | ]) 53 | 54 | PKG_CHECK_MODULES([libvarnishapi], [varnishapi]) 55 | PKG_CHECK_VAR([LIBVARNISHAPI_DATAROOTDIR], [varnishapi], [datarootdir]) 56 | PKG_CHECK_VAR([LIBVARNISHAPI_BINDIR], [varnishapi], [bindir]) 57 | PKG_CHECK_VAR([LIBVARNISHAPI_SBINDIR], [varnishapi], [sbindir]) 58 | AC_SUBST([LIBVARNISHAPI_DATAROOTDIR]) 59 | 60 | # Varnish include files tree 61 | VARNISH_VMOD_INCLUDES 62 | VARNISH_VMOD_DIR 63 | VARNISH_VMODTOOL 64 | 65 | AC_PATH_PROG([VARNISHTEST], [varnishtest], [], 66 | [$LIBVARNISHAPI_BINDIR:$LIBVARNISHAPI_SBINDIR:$PATH]) 67 | AC_PATH_PROG([VARNISHD], [varnishd], [], 68 | [$LIBVARNISHAPI_SBINDIR:$LIBVARNISHAPI_BINDIR:$PATH]) 69 | 70 | AC_CONFIG_FILES([ 71 | Makefile 72 | src/Makefile 73 | ]) 74 | AC_OUTPUT 75 | -------------------------------------------------------------------------------- /example.vcl: -------------------------------------------------------------------------------- 1 | vcl 4.0; 2 | import std; 3 | import directors; 4 | import rtstatus; 5 | 6 | backend default { 7 | .host = "127.0.0.1"; 8 | .port = "8080"; 9 | } 10 | backend server1 { 11 | .host = "127.0.0.1"; 12 | .port ="8081"; 13 | } 14 | backend server2 { 15 | .host = "127.0.0.1"; 16 | .port = "8082"; 17 | } 18 | 19 | sub vcl_init { 20 | new bar = directors.round_robin(); 21 | bar.add_backend(server1); 22 | bar.add_backend(server2); 23 | bar.add_backend(default); 24 | } 25 | 26 | sub vcl_recv { 27 | if (req.url ~ "/rtstatus.json") { 28 | return(synth(700, "OK")); 29 | } 30 | if (req.url ~ "/rtstatus") { 31 | return(synth(800, "OK")); 32 | } 33 | } 34 | 35 | sub vcl_synth { 36 | if (resp.status == 700) { 37 | set resp.status = 200; 38 | set resp.http.Content-Type = "application/json; charset=utf-8"; 39 | synthetic(rtstatus.rtstatus()); 40 | return (deliver); 41 | } 42 | if (resp.status == 800) { 43 | set resp.status = 200; 44 | set resp.http.Content-Type = "text/html; charset=utf-8"; 45 | synthetic(rtstatus.html()); 46 | return (deliver); 47 | } 48 | } 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /m4/PLACEHOLDER: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varnish/libvmod-rtstatus/3c945939f3a72e01c30d58f3ec44442618827d8c/m4/PLACEHOLDER -------------------------------------------------------------------------------- /readmeAsWell.rst: -------------------------------------------------------------------------------- 1 | Before reading this, please read the official README. 2 | This file is meant for a better understanding of the VMOD. 3 | 4 | The VMOD has two functions: 5 | 6 | rtstatus(REAL delta): this is the important one, it generates a JSON object containing a lot of varnish-counters.They are mostly raw, so you can use them as you prefer. 7 | The parameter *delta* is requested because is used for calculating hitrate and request load, the idea behind this is something like "I'd really like to know how my hitrate and my load have been doing in the last 13 seconds". *delta* has to be greater than 0 and smaller than 60 seconds; please remember the VMOD is called rtstatus where rt stands for real-time, values greater than one minutes just don't make sense for this vmod. 8 | I suggest to set delta to 5 or 10 seconds. 9 | 10 | html(): this function has been written for presenting the counters in a nice way. It is a c wrapper around some HTML/css and javascript. Basically I just wrote everything into a big string and then I used this string to feed the vmod function. 11 | Please feel free customize your frontend and if your idea is good, feel free to suggest it :) 12 | -------------------------------------------------------------------------------- /src/HTML/vmod_html.c: -------------------------------------------------------------------------------- 1 | #include "cache/cache.h" 2 | #include "vmod_rtstatus.h" 3 | #include "vmod_html.h" 4 | 5 | VCL_STRING 6 | vmod_html(const struct vrt_ctx *ctx) 7 | { 8 | struct iter_priv iter = { 0 }; 9 | iter.vsb = VSB_new(NULL, ctx->ws->f, WS_Reserve(ctx->ws, 0), VSB_AUTOEXTEND); 10 | VSB_cat(iter.vsb, html); 11 | VSB_finish(iter.vsb); 12 | if (VSB_error(iter.vsb)) { 13 | VSLb(ctx->vsl, SLT_VCL_Error, "VSB error"); 14 | WS_Release(ctx->ws, VSB_len(iter.vsb) + 1); 15 | return "{}"; 16 | } 17 | WS_Release(ctx->ws, VSB_len(iter.vsb) + 1); 18 | return (iter.vsb->s_buf); 19 | } 20 | 21 | -------------------------------------------------------------------------------- /src/HTML/vmod_html.h: -------------------------------------------------------------------------------- 1 | char *html = 2 | "\n" 3 | "\n" 4 | " \n" 5 | " \n" 6 | " \n" 7 | " \n" 8 | " \n" 9 | " \n" 10 | " \n" 11 | " Varnish Status\n" 12 | " \n" 13 | " \n" 14 | " \n" 15 | " \n" 16 | " \n" 17 | " \n" 18 | " \n" 22 | " \n" 23 | " \n" 24 | "
\n" 25 | "
\n" 26 | "
\n" 27 | " \n" 31 | "
\n" 32 | "
\n" 33 | "
\n" 34 | "\n" 35 | "
\n" 36 | "

VARNISH STATUS

\n" 37 | "
\n" 38 | "
\n" 39 | " \n" 40 | " \n" 41 | "
\n" 42 | "
\n" 43 | "
\n" 44 | "
\n" 45 | "
\n" 46 | " \n" 47 | " \n" 48 | " \n" 49 | " \n" 50 | " \n" 51 | " \n" 52 | " \n" 53 | " \n" 54 | " \n" 55 | " \n" 56 | " \n" 57 | " \n" 58 | " \n" 59 | " \n" 60 | " \n" 61 | " \n" 62 | " \n" 63 | "
BackendsNumReqConnUnhealthyBusyFailReuseToolateRecycleRetry
\n" 64 | "
\n" 65 | "\n" 66 | "\n" 67 | "
\n" 68 | "
\n" 69 | "
\n" 70 | " \n" 71 | " \n" 72 | " \n" 73 | " \n" 74 | " \n" 75 | " \n" 76 | " \n" 77 | " \n" 78 | " \n" 79 | " \n" 80 | " \n" 81 | " \n" 82 | " \n" 83 | " \n" 84 | " \n" 85 | "
Backend nameHealthReq KB/sRequestsResp KB/sResponsesPipe hdrPipe outPipe in
\n" 86 | " Show detailed counters\n" 87 | "
\n" 88 | "
\n" 89 | " \n" 90 | " \n" 91 | " \n" 92 | "
DescriptionNameValue
\n" 93 | "
\n" 94 | "
\n" 95 | "
\n" 96 | "
\n" 97 | "
\n" 98 | "
\n" 99 | "

©Copyright 2015 Varnish Software AS

\n" 100 | "
\n" 101 | "
\n" 102 | "\n" 203 | "\n" 204 | "\n" 205 | ; 206 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CPPFLAGS = @VMOD_INCLUDES@ -Wall -Werror 2 | 3 | vmoddir = @VMOD_DIR@ 4 | vmod_LTLIBRARIES = libvmod_rtstatus.la 5 | 6 | libvmod_rtstatus_la_LDFLAGS = -module -export-dynamic -avoid-version -shared $(VARNISHAPI_LIBS) 7 | libvmod_rtstatus_la_LIBADD = -lvarnishapi 8 | 9 | libvmod_rtstatus_la_SOURCES = \ 10 | vmod_rtstatus.h \ 11 | vmod_rtstatus.c \ 12 | vmod_rtstatus_cache.c \ 13 | HTML/vmod_html.h \ 14 | HTML/vmod_html.c 15 | 16 | nodist_libvmod_rtstatus_la_SOURCES = \ 17 | vcc_if.c \ 18 | vcc_if.h 19 | 20 | vmod_rtstatus.lo: vcc_if.c vcc_if.h 21 | 22 | vcc_if.c: vcc_if.h 23 | 24 | vcc_if.h: @VMODTOOL@ $(top_srcdir)/src/vmod_rtstatus.vcc 25 | @VMODTOOL@ $(top_srcdir)/src/vmod_rtstatus.vcc 26 | 27 | VMOD_TESTS = $(top_srcdir)/src/tests/*.vtc 28 | .PHONY: $(VMOD_TESTS) 29 | 30 | $(top_srcdir)/src/tests/*.vtc: 31 | @VARNISHTEST@ -Dvarnishd=@VARNISHD@ -Dvmod_topbuild=$(abs_top_builddir) $@ 32 | 33 | 34 | check: $(VMOD_TESTS) 35 | 36 | EXTRA_DIST = \ 37 | vmod_rtstatus.vcc\ 38 | $(VMOD_TESTS) 39 | 40 | CLEANFILES = \ 41 | $(builddir)/vcc_if.c \ 42 | $(builddir)/vcc_if.h \ 43 | $(builddir)/vmod_rtstatus.rst \ 44 | $(builddir)/vmod_rtstatus.man.rst 45 | -------------------------------------------------------------------------------- /src/tests/test01.vtc: -------------------------------------------------------------------------------- 1 | varnishtest "Test rtstatus vmod" 2 | server s1 { 3 | rxreq 4 | txresp -body "012345\n" 5 | accept 6 | rxreq 7 | txresp -body "012345\n" 8 | accept 9 | rxreq 10 | txresp -body "012345\n" 11 | accept 12 | rxreq 13 | txresp -body "012345\n" 14 | accept 15 | rxreq 16 | txresp -body "012345\n" 17 | accept 18 | rxreq 19 | txresp -body "012345\n" 20 | } -start 21 | 22 | varnish v1 -vcl+backend { 23 | sub vcl_hit { 24 | return (restart); 25 | } 26 | 27 | sub vcl_synth { 28 | if (req.restarts == 2) { 29 | set resp.status = 200; 30 | set resp.reason = "restart=2"; 31 | } elsif (req.restarts > 2) { 32 | set resp.status = 501; 33 | set resp.reason = "restart>2"; 34 | } elsif (req.restarts < 2) { 35 | set resp.status = 500; 36 | set resp.reason = "restart<2"; 37 | } 38 | } 39 | } -start 40 | 41 | varnish v1 -cliok "param.set max_restarts 2" 42 | 43 | client c1 { 44 | txreq -url "/" 45 | rxresp 46 | expect resp.status == 200 47 | } -run 48 | 49 | varnish v1 -cliok "param.set max_restarts 3" 50 | 51 | client c1 { 52 | txreq -url "/" 53 | rxresp 54 | expect resp.status == 501 55 | } -run 56 | 57 | varnish v1 -cliok "param.set max_restarts 1" 58 | 59 | client c1 { 60 | txreq -url "/" 61 | rxresp 62 | expect resp.status == 500 63 | } -run 64 | -------------------------------------------------------------------------------- /src/tests/test03.vtc: -------------------------------------------------------------------------------- 1 | varnishtest "Test rtstatus not proper url" 2 | 3 | server s1 { 4 | rxreq 5 | txresp 6 | } -start 7 | 8 | varnish v1 -vcl+backend { 9 | import rtstatus from "${vmod_topbuild}/src/.libs/libvmod_rtstatus.so"; 10 | 11 | sub vcl_recv { 12 | if (req.url !~ "/rtstatus") { 13 | return(synth(503, "KO")); 14 | } 15 | } 16 | } -start 17 | 18 | client c1 { 19 | txreq -url "/rtstati" 20 | rxresp 21 | expect resp.status == 503 22 | } -run 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/tests/test04.vtc: -------------------------------------------------------------------------------- 1 | varnishtest "Test rtstatus multiple varnish istances" 2 | 3 | server s1 { 4 | rxreq 5 | txresp 6 | } -start 7 | 8 | varnish v1 -vcl+backend { 9 | import rtstatus from "${vmod_topbuild}/src/.libs/libvmod_rtstatus.so"; 10 | 11 | sub vcl_recv { 12 | if (req.url ~ "/rtstatus") { 13 | return(synth(200, "OK")); 14 | } 15 | } 16 | sub vcl_synth { 17 | if (resp.status == 200) { 18 | synthetic(rtstatus.rtstatus(1)); 19 | return(deliver); 20 | } 21 | } 22 | } -start 23 | 24 | varnish v2 -vcl+backend { 25 | import rtstatus from "${vmod_topbuild}/src/.libs/libvmod_rtstatus.so"; 26 | 27 | sub vcl_recv { 28 | if (req.url !~ "/rtstatus") { 29 | return(synth(503, "KO")); 30 | } 31 | } 32 | sub vcl_synth { 33 | if (resp.status == 503) { 34 | set resp.http.Header = "KO"; 35 | return(deliver); 36 | } 37 | } 38 | 39 | } -start 40 | 41 | client c1 { 42 | txreq -url "/rtstatus" 43 | rxresp 44 | expect resp.status == 200 45 | } -start 46 | 47 | client c1 -connect ${v2_sock} { 48 | txreq -url "/rtstati" 49 | rxresp 50 | expect resp.status == 503 51 | expect resp.http.Header == "KO" 52 | } -run 53 | 54 | -------------------------------------------------------------------------------- /src/vmod_rtstatus.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "vapi/vsc.h" 8 | #include "vmod_rtstatus.h" 9 | 10 | uint64_t beresp_hdr, beresp_body; 11 | uint64_t bereq_hdr, bereq_body; 12 | static struct hitrate hitrate; 13 | static struct load load; 14 | int n_be, cont; 15 | 16 | double 17 | VTIM_mono(void) 18 | { 19 | #ifdef HAVE_GETHRTIME 20 | return (gethrtime() * 1e-9); 21 | #elif HAVE_CLOCK_GETTIME 22 | struct timespec ts; 23 | 24 | clock_gettime(CLOCK_MONOTONIC, &ts); 25 | return (ts.tv_sec + 1e-9 * ts.tv_nsec); 26 | #else 27 | struct timeval tv; 28 | 29 | gettimeofday(&tv, NULL); 30 | return (tv.tv_sec + 1e-6 * tv.tv_usec); 31 | #endif 32 | } 33 | 34 | int 35 | init_function(struct vmod_priv *priv, const struct VCL_conf *conf) 36 | { 37 | memset(&hitrate, 0, sizeof(struct hitrate)); 38 | memset(&load, 0, sizeof(struct load)); 39 | beresp_hdr = beresp_body = 0; 40 | bereq_hdr = bereq_body = 0; 41 | n_be = 0; 42 | cont = 0; 43 | return (0); 44 | } 45 | 46 | static void 47 | update_counter(struct counter *counter, double val) 48 | { 49 | if (counter->n < counter->nmax) 50 | counter->n++; 51 | counter->acc += (val - counter->acc) / (double)counter->n; 52 | } 53 | 54 | void 55 | rate(struct iter_priv *iter, struct VSM_data *vd) 56 | { 57 | double hr, mr, ratio, tv, dt, reqload; 58 | struct VSC_C_main *VSC_C_main; 59 | uint64_t hit, miss; 60 | time_t up; 61 | int req; 62 | 63 | VSC_C_main = VSC_Main(vd, NULL); 64 | if (VSC_C_main == NULL) 65 | return; 66 | 67 | tv = VTIM_mono(); 68 | dt = tv - hitrate.tm; 69 | hitrate.tm = tv; 70 | 71 | hit = VSC_C_main->cache_hit; 72 | miss = VSC_C_main->cache_miss; 73 | hr = (hit - hitrate.hit) / dt; 74 | mr = (miss - hitrate.miss) / dt; 75 | hitrate.hit = hit; 76 | hitrate.miss = miss; 77 | 78 | if (hr + mr != 0) 79 | ratio = hr / (hr + mr); 80 | else 81 | ratio = 0; 82 | 83 | up = VSC_C_main->uptime; 84 | req = VSC_C_main->client_req; 85 | reqload = ((req - load.req) / dt); 86 | load.req = req; 87 | 88 | update_counter(&hitrate.hr, ratio); 89 | update_counter(&load.rl, reqload); 90 | 91 | VSB_printf(iter->vsb, "\t\"uptime\" : \"%d+%02d:%02d:%02d\",\n", 92 | (int)up / 86400, (int)(up % 86400) / 3600, 93 | (int)(up % 3600) / 60, (int)up % 60); 94 | VSB_printf(iter->vsb, "\t\"uptime_sec\": \"%.2f\",\n", (double)up); 95 | VSB_printf(iter->vsb, "\t\"hitrate\": \"%.2f\",\n", hitrate.hr.acc * 100); 96 | VSB_printf(iter->vsb, "\t\"load\": \"%.2f\",\n", load.rl.acc); 97 | VSB_printf(iter->vsb, "\t\"delta\": \"%.2f\",\n", iter->delta); 98 | } 99 | 100 | int 101 | json_status(void *priv, const struct VSC_point *const pt) 102 | { 103 | struct iter_priv *iter = priv; 104 | const struct VSC_section *sec; 105 | uint64_t val; 106 | 107 | if (pt == NULL) 108 | return (0); 109 | 110 | val = *(const volatile uint64_t *)pt->ptr; 111 | sec = pt->section; 112 | 113 | if (iter->jp) 114 | iter->jp = 0; 115 | else 116 | VSB_cat(iter->vsb, ",\n"); 117 | VSB_cat(iter->vsb, "\t\""); 118 | if (strcmp(sec->fantom->type, "")) { 119 | VSB_cat(iter->vsb, sec->fantom->type); 120 | VSB_cat(iter->vsb, "."); 121 | } 122 | if (strcmp(sec->fantom->ident, "")) { 123 | VSB_cat(iter->vsb, sec->fantom->ident); 124 | VSB_cat(iter->vsb, "."); 125 | } 126 | VSB_cat(iter->vsb, pt->desc->name); 127 | VSB_cat(iter->vsb, "\": {"); 128 | if (strcmp(sec->fantom->type, "")) { 129 | VSB_cat(iter->vsb, "\"type\": \""); 130 | VSB_cat(iter->vsb, sec->fantom->type); 131 | VSB_cat(iter->vsb, "\", "); 132 | } 133 | if (strcmp(sec->fantom->ident, "")) { 134 | VSB_cat(iter->vsb, "\"ident\": \""); 135 | VSB_cat(iter->vsb, sec->fantom->ident); 136 | VSB_cat(iter->vsb, "\", "); 137 | } 138 | VSB_cat(iter->vsb, "\"descr\": \""); 139 | VSB_cat(iter->vsb, pt->desc->sdesc); 140 | VSB_cat(iter->vsb, "\", "); 141 | VSB_printf(iter->vsb,"\"value\": %" PRIu64 "}", val); 142 | if (iter->jp) 143 | VSB_cat(iter->vsb, "\n"); 144 | return(0); 145 | } 146 | 147 | int 148 | creepy_math(void *priv, const struct VSC_point *const pt) 149 | { 150 | struct iter_priv *iter = priv; 151 | const struct VSC_section *sec; 152 | uint64_t val; 153 | 154 | if (pt == NULL) 155 | return (0); 156 | 157 | val = *(const volatile uint64_t *)pt->ptr; 158 | sec = pt->section; 159 | if(!strcmp(sec->fantom->type,"MAIN")){ 160 | if(!strcmp(pt->desc->name, "n_backend")){ 161 | n_be = (int)val; 162 | } 163 | } 164 | if (!strcmp(sec->fantom->type, "VBE")) { 165 | if(!strcmp(pt->desc->name, "bereq_hdrbytes")) 166 | bereq_hdr = val; 167 | if(!strcmp(pt->desc->name, "bereq_bodybytes")) { 168 | bereq_body = val; 169 | VSB_cat(iter->vsb, "{\"ident\":\""); 170 | VSB_cat(iter->vsb, pt->section->fantom->ident); 171 | VSB_printf(iter->vsb,"\", \"bereq_tot\": %" PRIu64 ",", 172 | bereq_body + bereq_hdr); 173 | } 174 | 175 | if(!strcmp(pt->desc->name, "beresp_hdrbytes")) 176 | beresp_hdr = val; 177 | if(!strcmp(pt->desc->name, "beresp_bodybytes")) { 178 | beresp_body = val; 179 | VSB_printf(iter->vsb,"\"beresp_tot\": %" PRIu64 "}", 180 | beresp_body + beresp_hdr); 181 | if(cont < (n_be -1)) { 182 | VSB_cat(iter->vsb, ",\n\t\t"); 183 | cont++; 184 | } 185 | } 186 | } 187 | return(0); 188 | } 189 | 190 | int 191 | run_subroutine(struct iter_priv *iter, struct VSM_data *vd) 192 | { 193 | hitrate.hr.nmax = iter->delta; 194 | load.rl.nmax = iter->delta; 195 | VSB_cat(iter->vsb, "{\n"); 196 | rate(iter, vd); 197 | general_info(iter); 198 | backend(iter); 199 | VSB_cat(iter->vsb, "\t\"be_bytes\": ["); 200 | (void)VSC_Iter(vd, NULL, creepy_math, iter); 201 | VSB_cat(iter->vsb, "],\n"); 202 | cont = 0; 203 | (void)VSC_Iter(vd, NULL, json_status, iter); 204 | VSB_cat(iter->vsb, "\n}\n"); 205 | return(0); 206 | } 207 | 208 | -------------------------------------------------------------------------------- /src/vmod_rtstatus.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "vsb.h" 6 | #include "vrt.h" 7 | #include "vrt_obj.h" 8 | #include "vapi/vsm.h" 9 | 10 | struct iter_priv{ 11 | const struct vrt_ctx *cpy_ctx; 12 | struct vsb *vsb; 13 | int jp; 14 | double delta; 15 | }; 16 | struct counter { 17 | unsigned n, nmax; 18 | double acc; 19 | }; 20 | 21 | struct hitrate { 22 | double tm; 23 | uint64_t hit, miss; 24 | struct counter hr; /* hr stands for hitrate */ 25 | }; 26 | struct load { 27 | uint64_t req; 28 | struct counter rl; /* rl stands for reqload */ 29 | }; 30 | 31 | 32 | void WS_Release(struct ws *ws, unsigned bytes); 33 | unsigned WS_Reserve(struct ws *ws, unsigned bytes); 34 | int run_subroutine(struct iter_priv *iter, struct VSM_data *vd); 35 | int general_info(struct iter_priv *iter); 36 | int backend(struct iter_priv *iter); 37 | -------------------------------------------------------------------------------- /src/vmod_rtstatus.vcc: -------------------------------------------------------------------------------- 1 | $Module rtstatus 3 rtstatus VMOD 2 | $Init init_function 3 | $Function STRING rtstatus(REAL delta) 4 | $Function STRING html() 5 | -------------------------------------------------------------------------------- /src/vmod_rtstatus_cache.c: -------------------------------------------------------------------------------- 1 | #include "cache/cache.h" 2 | #include "vapi/vsm.h" 3 | #include "vcl.h" 4 | #include "cache/cache_backend.h" 5 | #include "vmod_rtstatus.h" 6 | 7 | int 8 | backend(struct iter_priv *iter) 9 | { 10 | const struct vrt_ctx *ctx = iter->cpy_ctx; 11 | int i; 12 | 13 | VSB_cat(iter->vsb, "\t\"backend\": ["); 14 | for (i = 1; i < iter->cpy_ctx->vcl->ndirector; ++i) { 15 | CHECK_OBJ_NOTNULL(ctx->vcl->director[i], DIRECTOR_MAGIC); 16 | if (strcmp("simple", ctx->vcl->director[i]->name) == 0) { 17 | char buf[1024]; 18 | int j, healthy; 19 | healthy = VDI_Healthy(ctx->vcl->director[i]); 20 | j = snprintf(buf, sizeof buf, "{\"director_name\" :" 21 | " \"%s\" , \"name\":\"%s\", \"value\": \"%s\"}", 22 | ctx->vcl->director[i]->name, 23 | ctx->vcl->director[i]->vcl_name, 24 | healthy ? "healthy" : "sick"); 25 | assert(j >= 0); 26 | VSB_cat(iter->vsb, buf); 27 | if (i < (ctx->vcl->ndirector - 1)) { 28 | VSB_cat(iter->vsb, ",\n\t\t"); 29 | } 30 | } 31 | } 32 | VSB_cat(iter->vsb, "],\n"); 33 | return(0); 34 | } 35 | 36 | int 37 | general_info(struct iter_priv *iter) 38 | { 39 | static char vrt_hostname[255] = ""; 40 | VSB_cat(iter->vsb, "\t\"varnish_version\" : \""); 41 | VSB_cat(iter->vsb, VCS_version); 42 | VSB_cat(iter->vsb, "\",\n"); 43 | gethostname(vrt_hostname, sizeof(vrt_hostname)); 44 | VSB_cat(iter->vsb, "\t\"server_id\": \""); 45 | VSB_cat(iter->vsb, vrt_hostname); 46 | VSB_cat(iter->vsb, "\",\n"); 47 | return(0); 48 | } 49 | 50 | VCL_STRING 51 | vmod_rtstatus(const struct vrt_ctx *ctx, VCL_REAL delta) 52 | { 53 | struct iter_priv iter = { 0 }; 54 | struct VSM_data *vd; 55 | 56 | if (delta < 1 || delta > 60) { 57 | VSLb(ctx->vsl, SLT_VCL_Error, "Delta has to be between" 58 | "1 and 60 seconds"); 59 | return "{}"; 60 | 61 | } 62 | vd = VSM_New(); 63 | if (VSM_Open(vd)) { 64 | VSLb(ctx->vsl, SLT_VCL_Error, "Can't open VSM"); 65 | VSM_Delete(vd); 66 | return "{}"; 67 | } 68 | iter.vsb = ctx->req->synth_body; 69 | iter.cpy_ctx = ctx; 70 | iter.jp = 1; 71 | iter.delta = delta; 72 | run_subroutine(&iter, vd); 73 | VSM_Delete(vd); 74 | return ""; 75 | 76 | } 77 | -------------------------------------------------------------------------------- /varnishstatus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varnish/libvmod-rtstatus/3c945939f3a72e01c30d58f3ec44442618827d8c/varnishstatus.png --------------------------------------------------------------------------------