{"id":19155933,"url":"https://github.com/lyokha/nginx-custom-counters-module","last_synced_at":"2025-10-13T01:21:23.324Z","repository":{"id":142525459,"uuid":"65078496","full_name":"lyokha/nginx-custom-counters-module","owner":"lyokha","description":"Nginx module featuring custom counters shared by worker processes and virtual servers","archived":false,"fork":false,"pushed_at":"2025-04-28T12:14:36.000Z","size":152,"stargazers_count":20,"open_issues_count":1,"forks_count":4,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-05-07T07:40:44.367Z","etag":null,"topics":["counter","nginx","shared-memory"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lyokha.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2016-08-06T10:55:51.000Z","updated_at":"2025-04-28T11:26:36.000Z","dependencies_parsed_at":null,"dependency_job_id":"62d9d577-c4cc-4731-adb9-9de4e3982bae","html_url":"https://github.com/lyokha/nginx-custom-counters-module","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/lyokha/nginx-custom-counters-module","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyokha%2Fnginx-custom-counters-module","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyokha%2Fnginx-custom-counters-module/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyokha%2Fnginx-custom-counters-module/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyokha%2Fnginx-custom-counters-module/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lyokha","download_url":"https://codeload.github.com/lyokha/nginx-custom-counters-module/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lyokha%2Fnginx-custom-counters-module/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279013886,"owners_count":26085325,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-12T02:00:06.719Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["counter","nginx","shared-memory"],"created_at":"2024-11-09T08:32:43.894Z","updated_at":"2025-10-13T01:21:23.318Z","avatar_url":"https://github.com/lyokha.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"Nginx Custom Counters module\n============================\n\n[![Build Status](https://github.com/lyokha/nginx-custom-counters-module/workflows/CI/badge.svg)](https://github.com/lyokha/nginx-custom-counters-module/actions?query=workflow%3ACI)\n\nThis Nginx module features customizable counters shared by all worker processes\nand, optionally, by configured sets of virtual servers.\n\nTable of contents\n-----------------\n\n- [Directives](#directives)\n- [Sharing between virtual servers](#sharing-between-virtual-servers)\n- [Collecting all counters in a single JSON object](#collecting-all-counters-in-a-single-json-object)\n- [Reloading Nginx configuration](#reloading-nginx-configuration)\n- [Persistent counters](#persistent-counters)\n- [Histograms](#histograms)\n- [Predefined counters](#predefined-counters)\n- [An example](#an-example)\n- [Remarks on using location ifs and complex conditions](#remarks-on-using-location-ifs-and-complex-conditions)\n- [Build and test](#build-and-test)\n- [See also](#see-also)\n\nDirectives\n----------\n\n*Normal counters* are updated on the latest request's *log phase*. They can be\ndeclared on *server*, *location*, and *location-if* configuration levels. Their\ncumulative values (except for references to run-time variables) are merged\nthrough all the levels from the top to the bottom when Nginx reads\nconfiguration.\n\n#### Normal counters synopsis\n\n```nginx\ncounter $cnt_name1 set 1;\ncounter $cnt_name2 inc $inc_cnt_name2;\ncounter $cnt_name2 undo;\n```\n\nVariables `$cnt_name1` and `$cnt_name2` can be accessed elsewhere in the\nconfiguration: they return values held in a shared memory and thus are equal\nacross all workers at the same moment. The second argument of the directive is\nan operation \u0026mdash; *set*, *inc* (i.e. increment), or *undo*. The third\nargument is applicable to *set* and *inc* operations only. This is an optional\ninteger value (possibly negative) or a variable (possibly negated), the default\nvalue is *1*. The *undo* operation discards all changes to the counter made on\nthe upper levels of the merged hierarchies.\n\nStarting from version *1.3* of the module, directive `counter` may declare\n*no-op* counters such as\n\n```nginx\ncounter $cnt_name3;\n```\n\nThis is the exact equivalent of directive `counter` with option `inc 0` which\ncan be used to declare variable `$cnt_name3` as a counter with the appropriate\nvariable handler while avoiding access to the shared memory in the run-time.\n\n*Early counters* are updated before *rewrite* directives and can be used to mark\nentry points before any *rewrites* and *ifs*. They are allowed only on\n*location* configuration level.\n\n#### Early counters synopsis\n\n```nginx\nearly_counter $cnt_name1 set 1;\nearly_counter $cnt_name2 inc $inc_cnt_name2;\nearly_counter $cnt_name2 undo;\n```\n\nMeaning of the arguments corresponds to that of the normal counters.\n\nEarly counters are capable to update on every *rewrite* jump to another\nlocation. With the following configuration,\n\n```nginx\nuser                    nobody;\nworker_processes        4;\n\nevents {\n    worker_connections  1024;\n}\n\nerror_log               /tmp/nginx-test-custom-counters-error.log warn;\n\nhttp {\n    default_type        application/octet-stream;\n    sendfile            on;\n\n    access_log          /tmp/nginx-test-custom-counters-access.log;\n\n    counters_survive_reload on;\n\n    server {\n        listen          8010;\n        server_name     main;\n\n        location / {\n            early_counter $cnt_1 inc;\n            rewrite ^ /2;\n        }\n\n        location /2 {\n            early_counter $cnt_1 inc;\n            early_counter $cnt_2 inc;\n            rewrite ^ /3;\n        }\n\n        location /3 {\n            echo \"cnt_1 = $cnt_1 | cnt_2 = $cnt_2\";\n        }\n    }\n}\n```\n\nall early counters will be printed expectedly.\n\n```ShellSession\n$ curl 'http://127.0.0.1:8010/'\ncnt_1 = 2 | cnt_2 = 1\n$ curl 'http://127.0.0.1:8010/'\ncnt_1 = 4 | cnt_2 = 2\n```\n\nA single counter can be declared both as normal and early if none of the merged\nlocation configuration hierarchies contains both types simultaneously.\n\nSharing between virtual servers\n-------------------------------\n\nCounters are shared between virtual servers if the latter have equal last\n*server names* that form an identifier for the counter set. The counter set\nidentifier may also be declared explicitly using directive `counter_set_id`\nwhich must precede all server's counters declarations.\n\nWhen a counter is not mentioned within a virtual server being a member of some\nother counter set, it gets *unreachable* in this virtual server. Unreachable\ncounters are displayed as empty strings, but this is configurable on *main* or\n*server* configuration levels via directive `display_unreachable_counter_as`,\ne.g.\n\n```nginx\n        display_unreachable_counter_as -;\n```\n\nCollecting all counters in a single JSON object\n-----------------------------------------------\n\nStarting from version *2.0* of the module, a new predefined variable\n`$cnt_collection` can be used to collect values of all counters from all counter\nsets and display them as a single JSON object. See [*an example*](#an-example).\n\nReloading Nginx configuration\n-----------------------------\n\nCounters *may* survive after Nginx configuration reload, provided directive\n`counters_survive_reload` was set on *main* or *server* configuration levels.\nCounters from a specific counter set *will not* survive if their number in the\nset has changed in the new configuration. Also avoid changing the order of\ncounters declarations, otherwise survived counters will pick values of their\nmates that were standing on these positions before reloading.\n\nPersistent counters\n-------------------\n\nCounters that survive reload may also be saved and loaded back when Nginx exits\nand starts respectively. To enjoy this feature, you need to add on *http*\nconfiguration level lines\n\n```nginx\n    counters_survive_reload on;\n    counters_persistent_storage /var/lib/nginx/counters.json 10s;\n```\n\nThe first directive can be moved inside *server* levels of the configuration\nwhere counters persistency is really wanted. Path */var/lib/nginx/counters.json*\ndenotes the directory where the counters will be saved. If the path is relative\n(i.e. it does not start with */*), then the counters will be saved in the\n*prefix* directory: run `nginx -h` to see where default prefix directory is\nlocated.\n\nValue *10s* defines time interval for saving persistent counters in a backup\nstorage. This argument is optional: if not set then the counters won't be\nwritten into the backup storage. The name of the backup file corresponds to the\nname of the main persistent storage with suffix *~* added. The file gets written\nby a worker process when a user request comes to a virtual server associated\nwith an existing counter set and the specified time interval from the last write\nhas elapsed.\n\nWriting to the backup storage can be useful to restore persistent counters on\npower outage or *kill -9* of the Nginx master process. In such cases the main\nstorage will be replaced by the backup storage automatically given that the\nlatter has more recent modification time and is not corrupted.\n\nPersistent counters require library [*JSMN*](https://github.com/zserge/jsmn),\nwhich is header-only. It means that for building them, you need to put file\n*jsmn.h* in the source directory of this module or in a standard system include\npath such as */usr/include*. If you want to enable building persistent counters,\nset environment variable `$NGX_HTTP_CUSTOM_COUNTERS_PERSISTENCY` to *y* or *yes*\nbefore or when running Nginx *configure* script, e.g.\n\n```ShellSession\n$ NGX_HTTP_CUSTOM_COUNTERS_PERSISTENCY=yes ./configure ...\n```\n\nHistograms\n----------\n\nHistograms provide a convenient way of dealing with a set of normal counters\nassociated with an arbitrary range.\n\n#### Synopsis\n\n```nginx\nhistogram $hst_name 12 $bound_var;\nhistogram $hst_name reuse;\nhistogram $hst_name undo;\nhistogram $hst_name reset;\n```\n\nThe upper line declares a histogram with *12* bins. The histogram must be bound\nto a variable to read the number of the bin to increment from. In this example,\nit's expected that variable `$bound_var` will return numbers in range *0 \u0026ndash;\n11* according to the number of the histogram bins. If it returns some unexpected\nvalue then variable `$hst_name_err` (which was declared implicitly) will be\nincremented instead of the range counters. The counters themselves and their\ncumulative count value can be accessed directly via implicitly declared\nvariables `$hst_name_00 .. $hst_name_11` and `$hst_name_cnt`. Notice that\nrarely, when shown in variable `$cnt_collection`, the error and the count values\ncan be very slightly inconsistent in relation to the range counters: this may\nhappen because all counters get updated independently, and the updates may occur\nin the middle of building of the collection when there are more than one worker\nprocesses.\n\nTo simplify detection of the bin to increment in the case of a contiguous value\ndistribution, directive `map_to_range_index` can be used. For example,\n\n```nginx\n    map_to_range_index $request_time $request_time_bin\n        0.005\n        0.01\n        0.05;\n```\n\nshall return in variable `$request_time_bin` values from *0* to *3* depending on\nthe value of variable `$request_time`: if the request time was less than or\nequal to *0.005* then its value will be *0*, otherwise, if the request time was\nless than or equal to *0.01* then its value will be *1*, and so later, finally,\nif the request time was more than *0.05* then its value will be *3*.\n\nA histogram with the same name may be declared only once in a counter set.\nSometimes it seems very restrictive, for example, when a histogram is supposed\nto collect data in two or more virtual servers. In this case, a histogram can be\nre-declared with directive *reuse*, given that it was already declared in this\ncounter set in upper lines of the configuration.\n\nHistograms layout can be observed via predefined variable `$cnt_histograms`.\n\nPredefined counters\n-------------------\n\nThere is a number of predefined counter variables: 7 counters from the Nginx\nstub status module (available only when Nginx was compiled with option\n*--with-http_stub_status_module*): `$cnt_stub_status_accepted`,\n`$cnt_stub_status_handled`, `$cnt_stub_status_requests`,\n`$cnt_stub_status_active`, `$cnt_stub_status_reading`,\n`$cnt_stub_status_writing`, `$cnt_stub_status_waiting`, and 4 counters related\nto the Nginx master process timing: `$cnt_uptime` and `$cnt_uptime_reload` which\ncontain the number of seconds elapsed since the start and the last reload of the\nmaster process respectively, and `$cnt_start_time` and `$cnt_start_time_reload`\nwhich contain the timestamps (in seconds elapsed since the start of the UNIX\nEpoch) of the start and the last reload of the master process respectively.\n\nAll predefined counters are not associated with any counter set identifier, nor\nare they collected in variable `$cnt_collection`.\n\nAn example\n----------\n\n```nginx\nuser                    nobody;\nworker_processes        4;\n\nevents {\n    worker_connections  1024;\n}\n\nerror_log               /tmp/nginx-test-custom-counters-error.log warn;\n\nhttp {\n    default_type        application/octet-stream;\n    sendfile            on;\n\n    access_log          /tmp/nginx-test-custom-counters-access.log;\n\n    map_to_range_index $request_time $request_time_bin\n        0.005\n        0.01\n        0.05\n        0.1\n        0.5\n        1.0\n        5.0\n        10.0\n        30.0\n        60.0;\n\n    counters_survive_reload on;\n\n    server {\n        listen          8010;\n        server_name     main;\n\n        counter $cnt_all_requests inc;\n\n        set $inc_a_requests 0;\n        if ($arg_a) {\n            set $inc_a_requests 1;\n        }\n\n        location / {\n            return 200;\n        }\n\n        counter $cnt_a_requests inc $inc_a_requests;\n\n        counter $cnt_test1_requests inc;\n        counter $cnt_test2_requests inc;\n        counter $cnt_test3_requests inc;\n\n        location /test {\n            counter $cnt_test_requests inc;\n            if ($arg_a) {\n                counter $cnt_test_a_requests inc;\n                break;\n            }\n            if ($arg_b) {\n                counter $cnt_test_b_requests inc;\n                return 200;\n            }\n            echo \"All requests before this: $cnt_all_requests\";\n        }\n\n        location /test/rewrite {\n            early_counter $ecnt_test_requests inc;\n            rewrite ^ /test last;\n        }\n\n        counter $cnt_bytes_sent inc $bytes_sent;\n    }\n\n    server {\n        listen          8020;\n        server_name     monitor.main;\n        counter_set_id  main;\n\n        allow 127.0.0.1;\n        deny  all;\n\n        location / {\n            echo -n \"all = $cnt_all_requests\";\n            echo -n \" | all?a = $cnt_a_requests\";\n            echo -n \" | /test = $cnt_test_requests\";\n            echo -n \" | /test?a = $cnt_test_a_requests\";\n            echo -n \" | /test?b = $cnt_test_b_requests\";\n            echo    \" | /test/rewrite = $ecnt_test_requests\";\n        }\n\n        location ~* ^/reset/a/(\\d+)$ {\n            set $set_a_requests $1;\n            counter $cnt_a_requests set $set_a_requests;\n            counter $cnt_test_a_requests set $set_a_requests;\n            return 200;\n        }\n\n        location /bytes_sent {\n            echo \"bytes_sent = $cnt_bytes_sent\";\n        }\n\n        location /all {\n            default_type application/json;\n            echo $cnt_collection;\n        }\n\n        location /histograms {\n            default_type application/json;\n            echo $cnt_histograms;\n        }\n    }\n\n    server {\n        listen          8030;\n        server_name     other;\n\n        counter $cnt_test1_requests inc;\n\n        display_unreachable_counter_as -;\n\n        location / {\n            echo \"all = $cnt_all_requests\";\n        }\n    }\n\n    server {\n        listen          8040;\n        server_name     test.histogram;\n\n        histogram $hst_request_time 11 $request_time_bin;\n\n        location / {\n            echo_sleep 0.5;\n            echo Ok;\n        }\n\n        location /1 {\n            echo_sleep 1;\n            echo Ok;\n        }\n    }\n\n    server {\n        listen          8050;\n        server_name     monitor.test.histogram;\n        counter_set_id  test.histogram;\n\n        location / {\n            echo \"all bins: $hst_request_time\";\n            echo \"bin 04:   $hst_request_time_04\";\n        }\n\n        location /reset {\n            histogram $hst_request_time reset;\n            echo Ok;\n        }\n    }\n}\n```\n\nLet's run some curl tests.\n\n```ShellSession\n$ curl 'http://127.0.0.1:8020/'\nall = 0 | all?a = 0 | /test = 0 | /test?a = 0 | /test?b = 0 | /test/rewrite = 0\n$ curl 'http://127.0.0.1:8010/test'\nAll requests before this: 0\n$ curl 'http://127.0.0.1:8020/'\nall = 1 | all?a = 0 | /test = 1 | /test?a = 0 | /test?b = 0 | /test/rewrite = 0\n$ curl 'http://127.0.0.1:8010/?a=1'\n$ curl 'http://127.0.0.1:8020/'\nall = 2 | all?a = 1 | /test = 1 | /test?a = 0 | /test?b = 0 | /test/rewrite = 0\n$ curl 'http://127.0.0.1:8010/test?b=1'\n$ curl 'http://127.0.0.1:8020/'\nall = 3 | all?a = 1 | /test = 2 | /test?a = 0 | /test?b = 1 | /test/rewrite = 0\n$ curl 'http://127.0.0.1:8010/test?b=1\u0026a=2'\nAll requests before this: 3\n$ curl 'http://127.0.0.1:8020/'\nall = 4 | all?a = 2 | /test = 3 | /test?a = 1 | /test?b = 1 | /test/rewrite = 0\n$ curl 'http://127.0.0.1:8010/test/rewrite?b=1\u0026a=2'\nAll requests before this: 4\n$ curl 'http://127.0.0.1:8020/'\nall = 5 | all?a = 3 | /test = 4 | /test?a = 2 | /test?b = 1 | /test/rewrite = 1\n$ curl 'http://127.0.0.1:8020/reset/a/0'\n$ curl 'http://127.0.0.1:8020/'\nall = 5 | all?a = 0 | /test = 4 | /test?a = 0 | /test?b = 1 | /test/rewrite = 1\n$ curl 'http://127.0.0.1:8020/reset/a/9'\n$ curl 'http://127.0.0.1:8020/'\nall = 5 | all?a = 9 | /test = 4 | /test?a = 9 | /test?b = 1 | /test/rewrite = 1\n```\n\nNow let's see how many bytes were sent by Nginx so far.\n\n```ShellSession\n$ curl 'http://127.0.0.1:8020/bytes_sent'\nbytes_sent = 949\n```\n\nAnd finally, let's see all counters at once.\n\n```ShellSession\n$ curl -s 'http://127.0.0.1:8020/all' | jq\n{\n  \"main\": {\n    \"cnt_all_requests\": 5,\n    \"cnt_a_requests\": 9,\n    \"cnt_test1_requests\": 5,\n    \"cnt_test2_requests\": 5,\n    \"cnt_test3_requests\": 5,\n    \"cnt_test_requests\": 4,\n    \"cnt_test_a_requests\": 9,\n    \"cnt_test_b_requests\": 1,\n    \"ecnt_test_requests\": 1,\n    \"cnt_bytes_sent\": 949\n  },\n  \"other\": {\n    \"cnt_test1_requests\": 0\n  },\n  \"test.histogram\": {\n    \"hst_request_time_00\": 0,\n    \"hst_request_time_01\": 0,\n    \"hst_request_time_02\": 0,\n    \"hst_request_time_03\": 0,\n    \"hst_request_time_04\": 0,\n    \"hst_request_time_05\": 0,\n    \"hst_request_time_06\": 0,\n    \"hst_request_time_07\": 0,\n    \"hst_request_time_08\": 0,\n    \"hst_request_time_09\": 0,\n    \"hst_request_time_10\": 0,\n    \"hst_request_time_cnt\": 0,\n    \"hst_request_time_err\": 0\n  }\n}\n```\n\nIt's time to test our histogram.\n\n```ShellSession\n$ for in in {1..20} ; do curl -D- 'http://localhost:8040/' \u0026 done\n  ...\n$ for in in {1..50} ; do curl -D- 'http://localhost:8040/1' \u0026 done\n  ...\n```\n\nLocations */* and */1* in the virtual server *test.histogram* delay responses\nfor *0.5* and *1* seconds respectively. We can check this from the values of the\nhistogram counters.\n\n```ShellSession\n$ curl -s 'http://127.0.0.1:8020/all' | jq {\\\"test.histogram\\\"}\n{\n  \"test.histogram\": {\n    \"hst_request_time_00\": 0,\n    \"hst_request_time_01\": 0,\n    \"hst_request_time_02\": 0,\n    \"hst_request_time_03\": 0,\n    \"hst_request_time_04\": 13,\n    \"hst_request_time_05\": 45,\n    \"hst_request_time_06\": 12,\n    \"hst_request_time_07\": 0,\n    \"hst_request_time_08\": 0,\n    \"hst_request_time_09\": 0,\n    \"hst_request_time_10\": 0,\n    \"hst_request_time_cnt\": 70,\n    \"hst_request_time_err\": 0\n  }\n}\n```\n\nFrom this output, we can see that there were *70* requests spread in bins\n*04 \u0026ndash; 06* which correspond approximately to a time range from *0.5* to\n*1-and-more* seconds.\n\nLet's see how to access all the bins at once and a specific bin.\n\n```ShellSession\n$ curl 'http://127.0.0.1:8050/'\nall bins: 0,0,0,0,13,45,12,0,0,0,0\nbin 04:   13\n```\n\nAnd we also have a way to reset the histogram.\n\n```ShellSession\n$ curl -s 'http://127.0.0.1:8050/reset'\nOk\n$ curl -s 'http://127.0.0.1:8020/all' | jq {\\\"test.histogram\\\"}\n{\n  \"test.histogram\": {\n    \"hst_request_time_00\": 0,\n    \"hst_request_time_01\": 0,\n    \"hst_request_time_02\": 0,\n    \"hst_request_time_03\": 0,\n    \"hst_request_time_04\": 0,\n    \"hst_request_time_05\": 0,\n    \"hst_request_time_06\": 0,\n    \"hst_request_time_07\": 0,\n    \"hst_request_time_08\": 0,\n    \"hst_request_time_09\": 0,\n    \"hst_request_time_10\": 0,\n    \"hst_request_time_cnt\": 0,\n    \"hst_request_time_err\": 0\n  }\n}\n```\n\nThough is not present in this example, histogram operation *undo* disables\nchanging the histogram in the scope where it is declared.\n\nFinally, we can examine how all histograms lay out in counter sets.\n\n```ShellSession\n$ curl -s 'http://127.0.0.1:8020/histograms' | jq\n{\n  \"main\": {},\n  \"other\": {},\n  \"test.histogram\": {\n    \"hst_request_time\": {\n      \"range\": {\n        \"hst_request_time_00\": \"0.005\",\n        \"hst_request_time_01\": \"0.01\",\n        \"hst_request_time_02\": \"0.05\",\n        \"hst_request_time_03\": \"0.1\",\n        \"hst_request_time_04\": \"0.5\",\n        \"hst_request_time_05\": \"1.0\",\n        \"hst_request_time_06\": \"5.0\",\n        \"hst_request_time_07\": \"10.0\",\n        \"hst_request_time_08\": \"30.0\",\n        \"hst_request_time_09\": \"60.0\",\n        \"hst_request_time_10\": \"+Inf\"\n      },\n      \"cnt\": [\n        \"hst_request_time_cnt\",\n        \"cnt\"\n      ],\n      \"err\": [\n        \"hst_request_time_err\",\n        \"err\"\n      ]\n    }\n  }\n}\n```\n\nRemarks on using location ifs and complex conditions\n----------------------------------------------------\n\nOriginally in Nginx, *location ifs* were designed for a very special task:\n*replacing location configuration* when a given condition matches, not for\n*doing anything*. That's why using them for only checking a counter like when\ntesting against `$arg_a` in location */test* is a bad idea in general. In\ncontrast, *server ifs* do not change location configurations and can be used for\nchecking increment or set values like `$inc_a_requests`. In our example we can\nsimply replace *location if* test\n\n```nginx\n            if ($arg_a) {\n                counter $cnt_test_a_requests inc;\n                break;\n            }\n```\n\nwith\n\n```nginx\n            counter $cnt_test_a_requests $inc_a_requests;\n```\n\nHowever *if* condition testing in Nginx is not as powerful as it may be\nrequired. If you need a complex condition testing then consider binding\nincrement or set variables to a full-featured programming language's handler.\nFor example, let's increment a counter when a *base64*-encoded value contains a\nversion tag like *v=1*. To make all required computations, let's use Haskell and\n[*Nginx Haskell module*](http://github.com/lyokha/nginx-haskell-module).\n\nPut directive `haskell compile` with Haskell function *hasVTag* on *http*\nconfiguration level.\n\n```nginx\n    haskell compile standalone /tmp/ngx_haskell.hs '\n\nimport Data.ByteString.Base64\nimport Data.Maybe\nimport Text.Regex.PCRE\n\nhasVTag = either (const False) (matchTest r) . decode\n    where r = makeRegex \"\\\\\\\\bv=\\\\\\\\d+\\\\\\\\b\" :: Regex\n\nNGX_EXPORT_B_Y (hasVTag)\n\n    ';\n```\n\nThen put the next 2 lines into location */test*.\n\n```nginx\n            haskell_run hasVTag $hs_inc_cnt_vtag $cookie_misc;\n            counter $cnt_test_cookie_misc_vtag inc $hs_inc_cnt_vtag;\n```\n\nCounter *cnt_test_cookie_misc_vtag* increments when value of *cookie Misc*\nmatches against a version tag compiled as a regular expression with *makeRegex*.\n\nBy adding another line with *echo*\n\n```nginx\n            echo -n \" | /test?misc:vtag = $cnt_test_cookie_misc_vtag\";\n```\n\ninto location */* in the second virtual server, the counter gets monitored along\nwith other custom counters.\n\nTo test this, run\n\n```ShellSession\n$ curl -b 'Misc=bW9kZT10ZXN0LHY9Mg==' 'http://localhost:8010/test'\n```\n\n(value *bW9kZT10ZXN0LHY9Mg==* is base64-encoded string *mode=test,v=2*, try\nother variants with or without a version tag too), and watch the value of the\ncounter *cnt_test_cookie_misc_vtag*.\n\nBuild and test\n--------------\n\nThe module is built with the standard Nginx build approach from the directory\nwith Nginx source files. If you want to link this module with the Nginx\nexecutable file statically, use *configure* option *--add-module*, e.g.\n\n```ShellSession\n$ ./configure --add-module=/path/to/this/module\n$ make\n$ sudo make install\n```\n\nTo use the module as a dynamic library, choose option *--add-dynamic-module*.\n\n```ShellSession\n$ ./configure --add-dynamic-module=/path/to/this/module\n$ make\n$ sudo make install\n```\n\nIn the latter case, put directive\n\n```nginx\nload_module modules/ngx_http_custom_counters_module.so\n```\n\nin the Nginx configuration file.\n\nTo benefit from persistent counters, set environment variable\n*$NGX_HTTP_CUSTOM_COUNTERS_PERSISTENCY* to *y* or *yes*.\n\n```ShellSession\n$ NGX_HTTP_CUSTOM_COUNTERS_PERSISTENCY=yes ./configure --add-module=/path/to/this/module\n```\n\nThe configuration script tests the availability of the *JSMN* library\nautomatically.\n\nWith command *prove* from Perl module *Test::Harness* and Perl module\n*Test::Nginx::Socket*, tests can be run by a regular user from directory\n*test/*.\n\n```ShellSession\n$ prove t/basic.t t/check-persistency.t\n```\n\nAdd option *-v* for verbose output. Before run, you may need to adjust\nenvironment variable *PATH* to point to the Nginx installation directory.\n\nNote that the tests in directory *test/* expect that the module was built with\nsupport of persistent counters.\n\nSee also\n--------\n\n1. [*Универсальные счетчики в nginx: замечания к реализации\nмодуля*](http://lin-techdet.blogspot.com/2016/08/nginx.html) (in Russian).\nRemarks on implementation of the module.\n2. Haskell module\n[*NgxExport.Tools.Prometheus*](https://github.com/lyokha/ngx-export-tools-extra#module-ngxexporttoolsprometheus)\nto convert custom counters from this module to Prometheus metrics.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flyokha%2Fnginx-custom-counters-module","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flyokha%2Fnginx-custom-counters-module","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flyokha%2Fnginx-custom-counters-module/lists"}