{"id":21707812,"url":"https://github.com/flightaware/prometheus-tcl","last_synced_at":"2026-03-11T01:02:18.729Z","repository":{"id":66872747,"uuid":"329649452","full_name":"flightaware/prometheus-tcl","owner":"flightaware","description":"Pure Tcl Prometheus Client Library","archived":false,"fork":false,"pushed_at":"2025-12-11T22:14:46.000Z","size":81,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-12-14T17:59:37.307Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Tcl","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/flightaware.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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-01-14T15:04:50.000Z","updated_at":"2021-02-10T05:20:11.000Z","dependencies_parsed_at":"2023-02-23T10:46:10.504Z","dependency_job_id":null,"html_url":"https://github.com/flightaware/prometheus-tcl","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/flightaware/prometheus-tcl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flightaware%2Fprometheus-tcl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flightaware%2Fprometheus-tcl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flightaware%2Fprometheus-tcl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flightaware%2Fprometheus-tcl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/flightaware","download_url":"https://codeload.github.com/flightaware/prometheus-tcl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flightaware%2Fprometheus-tcl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30364608,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T21:41:54.280Z","status":"ssl_error","status_checked_at":"2026-03-10T21:40:59.357Z","response_time":106,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":[],"created_at":"2024-11-25T22:19:23.370Z","updated_at":"2026-03-11T01:02:18.679Z","avatar_url":"https://github.com/flightaware.png","language":"Tcl","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tcl Prometheus Package\n\nThe code in this directory provides the `prometheus-tcl` package: a pure Tcl library for instrumenting Tcl scripts with [Prometheus metrics](https://prometheus.io/docs/concepts/metric_types/).\n\n# Getting Started\n\nAfter running the provided `install` make target, import the `prometheus-tcl` package like usual\n\n```\npackage require prometheus-tcl\n```\n\nThis provides a number of procs in the `::prom` namespace (abbreviated for ease of typing).\n\n## Dependencies\n\n`prometheus-tcl` is written for Tcl 8.6 and requires a minimal set of dependencies:\n\n- `cmdline` (argument parsing)\n- `TclOO` (organizes the code providing the client API)\n- `Thread` (thread safe metric operations)\n- `zlib` (compressing HTTP replies)\n- `tls` (only required if exposing metrics over HTTPS, either push or pull)\n- `http` (only required if pushing metrics)\n- `base64` (only required if pushing metrics)\n- `uri` (only required if pushing metrics)\n\nAnd, for the unit tests:\n\n- `struct::list`\n- `math`\n\n## Creating Metrics\n\nBefore using a metric, declare it with one of the **`new`** procs:\n\n```\n# Create a Counter named messages_processed_total without any labels\nprom::counter::new messages_processed_total -help \"Number of input messages processed\"\n\n# Create a Counter named http_requests_total with some labels\nprom::counter::new http_requests_total -help \"HTTP requests by method and code\" -labels {method code}\n\n# Create a Gauge named rate_limit_queue_size\nprom::gauge::new rate_limit_queue_size -help \"Number of enqueued messages waiting to be processed\"\n\n# Create a Histogram with a method label\nprom::histogram::new http_request_duration_seconds -labels {method} -buckets {0.05 0.1 0.2 0.5 1}\n\n# Create an Info metric to track build info\nprom::info::new application_build -help \"Build info for application\" -labels {branch version} \n\n# Create a Summary for request duration\nprom::summary::new microservice_rpc_duration_seconds -help \"Microservice RPC duration in seconds\"\n```\n\nWhen defining a metric with labels, only the label keys should be provided.\n\nCalling **`new`** more than once for the same metric name throws an error (unless a suitable [policy](#name-conflict-policy) has been set to override that behavior).\n\nLikewise, trying to use a metric without a prior call to **`new`** throws an error (unless a suitable [policy](#name-missing-policy) has been set to override that behavior).\n\nAlso, providing an [invalid metric name or label key](https://prometheus.io/docs/concepts/data_model/) throws an error.\n\nIf no errors occur, the **`new`** procs return the empty string.\n\nAfter declaring a metric, manipulate its value by passing its name to one of [the procs detailed in a later section](#using-created-metrics).\n\n### Provided **`new`** Procs\n\n**prom::counter::new** *metricName* ?-**help** *helpText*? ?-**namespace** *metricNamePrefix*? ?-**labels** *labelKeys*? ?-**timestamp**?\n\n**prom::gauge::new** *metricName* ?-**help** *helpText*? ?-**namespace** *metricNamePrefix*? ?-**labels** *labelKeys*? ?-**timestamp**? ?-**mergePolicy** *policy*?\n\n**prom::histogram::new** *metricName* ?-**help** *helpText*? ?-**namespace** *metricNamePrefix*? ?-**labels** *labelKeys*? ?-**timestamp**? ?-**buckets** *bucketBoundaries*\n\n**prom::info::new** *metricName* ?-**help** *helpText*? ?-**namespace** *metricNamePrefix*? ?-**labels** *labelKeys*? ?-**timestamp**?\n\n**prom::summary::new** *metricName* ?-**help** *helpText*? ?-**namespace** *metricNamePrefix*? ?-**labels** *labelKeys*? ?-**timestamp**?\n\n\n#### Common Arguments\n\nGiven a [valid metric name](https://prometheus.io/docs/concepts/data_model/), *metricName*, **`new`** creates a [Counter](https://www.robustperception.io/how-does-a-prometheus-counter-work), [Gauge](https://www.robustperception.io/how-does-a-prometheus-gauge-work), [Histogram](https://www.robustperception.io/how-does-a-prometheus-histogram-work), [Info](https://www.robustperception.io/how-to-have-labels-for-machine-roles) or [Summary](https://www.robustperception.io/how-does-a-prometheus-summary-work) metric.\n\nTo set the [HELP description](https://prometheus.io/docs/instrumenting/exposition_formats/#comments-help-text-and-type-information) for the metric, provide the optional -**help** argument.  If no -**help** is provided, the *metricName* will be used.  The [Prometheus documentation](https://prometheus.io/docs/instrumenting/writing_clientlibs/#metric-description-and-help) is fairly strict about requiring help text, but `prometheus-tcl` is not.  Help text is recommended but not required.  \n\nFor specifying the metric's labels, provide a list of label keys, *labelKeys*, to the -**label** argument.  The order of elements in *labelKeys* is important: label values will need to be passed in that same order when calling the procs [detailed below](#using-created-metrics) for using the metrics, e.g., incrementing it or observing a value.  \n\nOptionally the -**namespace** argument can contain a string *metricNamePrefix* that will be prepended (along with an underscore) to the *metricName* passed to a **`new`** proc.  Although a -**namespace** can be provided to **`new`**, the preferred way is to [set a default global namespace value](#namespaces) for metrics created with **`new`**.\n\nLastly, if -**timestamp** is provided, then each modification to the metric will also be accompanied by an epoch timestamp in milliseconds.  This timestamp will show up in the data provided at scrape time, e.g., by calling `prom::collect`.\n\n#### Gauge Specific\n\nIf using `prometheus-tcl` in a multi-threaded application, it is possible to merge the metrics across some subset of Tcl threads.  In that case, [merging metrics](#merge-policy) is fairly straightforward, except for gauges where it is not always clear what the best option is.  For that situation, the -**metricPolicy** option exists.  For *policy* it accepts `max` (the default), `min` or `sum`.\n\n#### Histogram Specific\n\nThe default buckets for a histogram are `{.005 .01 .025 .05 .1 .25 .5 1 2.5 5 10}`.  These are taken from the [golang client](https://github.com/prometheus/client_golang/blob/master/prometheus/histogram.go#L57-L67).  See the link for further explanation. \n\nTo override the defaults, provide a list of numbers, *bucketBoundaries*, sorted in increasing order to the -**buckets** argument.\n\n*bucketBoundaries* must contain at least one value excluding `Inf`.\n\nThe `Inf` bucket does not need to be explicitly provided.\n\n##### Bucket Boundary Creation\n\nTwo helper procs ease common-case bucket creation.  \n\nBoth helper procs return a list of bucket boundaries.\n\n###### Linear Buckets\n\nFor consecutive bucket boundaries separated by a common difference\n\n**prom::linear_buckets** *start* *width* *count*\n\n*count* must be greater than or equal to 1\n\n###### Exponential Buckets\n\nFor consecutive bucket boundaries separated by a common factor\n\n**prom::exponential_buckets** *start* *factor* *count*\n\n*start* must be greater than 0\n\n*count* must be greater than or equal to 1\n\n*factor* must be greater than 1\n\n###### Example Usage\n\n```\n# Example of linear buckets\nprom::linear_buckets 0 5 10\n\n# Returns the following list\n0 5 10 15 20 25 30 35 40 45\n\n# Example of exponential buckets\nprom::exponential_buckets 1 2 5\n\n# Returns the following list\n1 2 4 8 16\n```\n\n#### Info Specific\n\nInfo metrics are not part of the Prometheus standard.  Behind the scenes, they are actually Gauges with a value of `1.0.`.  \n\nHowever, it is common practice to use this style of metric for exposing information like [https://www.robustperception.io/exposing-the-software-version-to-prometheus](software versions) or [machine roles](http://www.robustperception.io/how-to-have-labels-for-machine-roles/).  \n\nThe **prom::info::new** proc takes the same arguments as the others (except for Histograms), but adds two wrinkles:\n\n- At least one label is required.  An error will be thrown if the -**labels** option value is an empty list.\n- If the *metricName* does not end with `_info`, `prometheus-tcl` will automatically append `_info` to it.  There is no way to disable this, so use a Gauge explicitly if this is not desired.\n\n#### Summary Specific\n\nNote that summary metric types in `prometheus-tcl` do **NOT** calculate Phi-quantiles.  Instead, they only provide the two Counters, `_sum` and `_count`.  As stated in the link above:\n\n\u003e Overall summarys without quantiles are a nice cheap way to track latencies, amount of data transferred per request, records accessed etc. as it only uses two time series per labelset. \n\n\n### Namespaces\n\nWhen declaring new metrics, a global namespace, which acts as a metric name prefix, can be set using the **`prom::set_namespace`** proc, which takes a single argument, *namepsace*.  By default the namespace is the empty string, but, if not, the value of *namespace* and an underscore will be prefixed to every metric name provided to a `new` proc.  \n\nWhen a non-empty namespace has been set, the full name of the metric is exposed when `prom::collect` is called.  Outside of that, though, the *namespace* value should not be provided in any `prometheus-tcl` proc calls requiring a metric name.\n\n```\n# Before declaring metrics, set the namespace\nprom::set_namespace flightaware\n\n# Create a metric whose full name will be flightaware_departures_total, but only as seen by Prometheus\nprom::counter::new departures_total -help \"Count total departures issued by FlightAware\" -labels {airline adhoc}\n\n# Use the metric without needing to mention the namespace\nprom::counter::inc departures_total $airline $adhoc \n\n# Providing the full name of the counter throws an error \nprom::counter::inc flightaware_departures_total $airline $adhoc\n\n# What is displayed during a scrape by Prometheus:\n\n# HELP departures_total Count total departures issued by FlightAware\n# TYPE departures_total counter\ndepartures_total{airline=\"1\",adhoc=\"1\"} 1.0\n```\n\n### Metric Name Callback\n\nWhen defining and using metrics, following [the Prometheus naming best practices](https://prometheus.io/docs/practices/naming/) can lead to fairly verbose metric names.  Typing the full metric name can become tedious, and when used with labels, can lead to very long line lengths.  To help address this problem somewhat, `prometheus-tcl` allows for setting a callback for controlling metric names that will be invoked at metric creation time, i.e., when calling a **`new`** proc:\n\n**prom::metic_name_callback** *callback*\n\nSet the callback by passing a non-empty string to *callback*. \n\nUnset any previously set callback by passing in `\"\"` as *callback*.\n\nThe callback will be passed two arguments, *metricType* and *metricName*.  \n\nThe callback must return a string representing the full metric name to use (excluding any namespace).\n\nWhen a metric name callback has been set and a metric has been declared, use the same value passed to **`new`** when interacting with the metric.\n\nFor example, to automatically append `_total` to any counter metrics:\n\n```\nproc counter_naming {metricType metricName} {\n\tif {$metricType eq \"counter\"} {\n\t\tset metricName ${metricName}_total\n\t}\n\n\treturn $metricName\n}\n\nprom::metric_name_callback counter_naming\n\n# When exposed to Prometheus, the name will be example_total\nprom::counter::new example\n\n# When using the counter, pass the same name provided to new\nprom::counter::inc example\n\n# Do not use the name returned by the metric name callback\n# It will traceback\nprom::counter::inc example_total; # error\n```\n\n### Name Conflict Policy\n\nWhile the default behavior of **`new`** is to throw an error if the metric name being declared already exists.  This can be modified by setting the name conflict policy to `ignore`\n\n**prom::set_name_conflict_policy** *policyName*\n\nIt supports a *policyName* of  `error` policy (the default) or `ignore` which silently returns without doing anything.\n\n### Collection Registry\n\nBy default `prometheus-tcl` registers every metric created with **`new`** into a default registry created at package load (a registry is a `prom::Registry` object).  This should cover the majority of cases.  \n\nFor more advanced scenarios where an alternate registry is needed use\n\n**prom::set_collection_registry** *registryObject*\n\nAfter setting the collection registry, all subsequent calls to `prom::collect` will use the provided *registryObject*.\n\n## Using Created Metrics\n\nOnce a metric has been declared with **`new`**, use its name to access the expected Prometheus operations.\n\nLabel values are provided after the metric name and the operation's exepected argument(s).  \n\nLabel values must be provided in the same order that their corresponding label keys were provided to the **`new`** command.\n\n### Counter\n\n**prom::counter::inc** *metricName* ?-**amount** *amount*? ?*labelValue ...*?\n\nIncrement *metricName* either by 1 (the default) or by some other non-negative *amount* provided to the -**amount** argument.  *amount* can be any value recognized by `string is double -strict`.\n\n### Gauge\n\n**prom::gauge::inc** *metricName* ?-**amount** *amount*? ?*labelValue ...*?\n\nIncrement *metricName* either by 1 (the default) or by some other non-negative *amount* using the -**amount** option.  *amount* can be any value recognized by `string is double -strict`.\n\n**prom::gauge::dec** *metricName* ?-**amount** *amount*? ?*labelValue ...*?\n\nDecrement *metricName* either by 1 (the default) or by a non-negative *amount* using the -**amount** option.  *amount* can be any value recognized by `string is double -strict`.\n\n**prom::gauge::set_value** *metricName* *value* ?*labelValue ...*? \n\nSet *metricName* to a particular numeric *value*.  *value* can be anything recognized by `string is double -strict`.\n\n**prom::gauge::set_to_current_time** *metricName* ?*labelValue ...*? \n\nSet *metricName* to the current epoch timestamp in seconds.\n\n**prom::gauge::time** *metricName* *script* ?*labelValue ...*? \n\nRun *script* using [Tcl's time command](https://www.tcl.tk/man/tcl8.6/TclCmd/time.htm), convert the result to seconds and set that value in the gauge named *metricName*.\n\n### Histogram\n\n**prom::histogram::observe** *metricName* *value* ?*labelValue ...*? \n\nObserve a *value* for the Histogram named *metricName*.\n\n**prom::histogram::time** *metricName* *script* ?*labelValue ...*? \n\nRun *script* using [Tcl's time command](https://www.tcl.tk/man/tcl8.6/TclCmd/time.htm), convert the result to seconds and observe that value in the histogram named *metricName*.\n\n### Summary\n\n**prom::summary::observe** *metricName* *value* ?*labelValue ...*? \n\nObserve a *value* for the Summary named *metricName*.\n\n**prom::summary::time** *metricName* *script* ?*labelValue ...*? \n\nRun *script* using [Tcl's time command](https://www.tcl.tk/man/tcl8.6/TclCmd/time.htm), convert the result to seconds and observe that value in the summary named *metricName*.\n\n### Info\n\n**prom::info::labels** *metricName* *labelValue* ?*labelValue ...*?\n\nSet the label values for the Info metric *metricName*.\n\nSince labels are required for creating Info metrics, at least one *labelValue* value must be provided.\n\n### Note About *`labelValue`* Arguments\n\nFor any proc detailed in [the section above](#using-created-metrics), when providing the label values for a given metric name, the values **MUST** be in the order provided to the -**labels** argument to **`new`**.\n\nFor example, assume we declare a Counter named `total` with three label keys, `k1`, `k2`, and `k3`, represented by the Tcl list `{k1 k2 k3}`:\n\n```\nprom::counter::new total -help \"Example of the importance of labelValue order\" -labels {k1 k2 k3}\n```\n\nWith `total` declared, assume we now want to increment `total` with label values `k2=\"v2\"`, `k3=\"v3\"`, and `k1=\"v1\"`.  Since we declared `total` with the *labelKeys* list `{k1 k2 k3}`, we need to provide label values in that order:\n\n```\nprom::counter::inc total v1 v2 v3\n```\n\n### Note About Timing\n\nThe **`time`** procs provided by **prom::gauge**, **prom::histogram** and **prom::summary** only support timing in seconds.  \n\nRestricting the units to seconds conforms with [Prometheus client guidelines](https://prometheus.io/docs/instrumenting/writing_clientlibs/) and the [metric naming best practices](https://prometheus.io/docs/practices/naming/).\n\n**`time`** only provides the wall time it took to execute the *script* argument.  If you need CPU time, see [`times` from the TclX extension](https://github.com/flightaware/tclx).\n\nImportantly, these procs do **NOT** catch exceptions; however, if an error is thrown while evaluating *script*, *metricName* will still be updated with *script*'s execution time.\n\n### Name Missing Policy\n\nBy default it is an error to use one of the procs in this section without a prior call to **`new`**.  \n\n`prometheus-tcl` can also silently ignore any attempt to manipulate an undeclared metric name by setting the missing name policy\n\n**prom::set_name_missing_policy** *policy*\n\nTo ignore any attempt to use an undeclared metric name, pass a value of `ignore` as the *policy* argument.\n\n## Collecting Metrics\n\n### Metrics As a String\n\nFor getting the metrics in Prometheus' text-based [exposition format](https://prometheus.io/docs/instrumenting/exposition_formats/) use\n\n**prom::collect**\n\nwhich takes no arguments and returns a Prometheus formatted string of the current metrics created using `prometheus-tcl`.\n\n`prometheus-tcl` only supports the Prometheus text format.\n\n### Metrics Over HTTP\n\nTo expose a HTTP port for Prometheus to scrape use \n\n**prom::expose** ?-**address** *address*? ?-**port** *port*? ?-**tls**? ?-**tlsArgs** *args*? ?-**path** *path*?\n\nRequires entering the event loop. \n\nBy default `prometheus-tcl` listens on the wildcard address on *port* `1347` for *path* `/metrics` where *path* is the [`request-target` of RFC7230](https://tools.ietf.org/html/rfc7230#section-3.1.1).\n\nThe -**address** option supports the same values that the Tcl `socket` command does for its `-myaddr` option.\n\nTLS support can be enabled with the boolean -**tls** argument.  To configure TLS, pass a single list of arguments to the -**tlsArgs** option.  It takes all arguments accepted by `tcltls`' [`tls::import` command](https://core.tcl-lang.org/tcltls/wiki?name=Documentation#tls::import).\n\nImportantly, if you use TLS with **prom::expose** and a request fails, e.g., a client does not even use TLS for a request, the [background exception handler](https://www.tcl-lang.org/man/tcl8.6/TclCmd/interp.htm#M10) is called, so be sure to set one when using this option.  The *message* argument passed to the background error handler for these protocol failures starts with the string `SSL channel`.\n\n*path* can be any glob pattern.  If a request is made to a URI that doesn't match *path*, a `400 Bad Request` is sent.\n\n### Metrics To A File\n\n**prom::collect_to_file** *filePath* \n\nwrites the results of `prom::collect` to a file in a non-blocking way.  It returns `0` if an error occurred or `1` if the file was written successfully. \n\nThis can be used, for instance, with the [`node_exporter`'s Textfile Collector](https://github.com/prometheus/node_exporter#textfile-collector).\n\n### PushGateway\n\nFor pushing metrics to [a PushGateway](https://github.com/prometheus/pushgateway) the following procs are provided:\n\n**prom::push_to_gateway** *gateway* *job* ?-**groupingKey** *labelsDict*? ?-**timeout** *timeoutMS*?\n\n**prom::pushadd_to_gateway** *gateway* *job* ?-**groupingKey** *labelsDict*? ?-**timeout** *timeoutMS*?\n\n**prom::delete_from_gateway** *gateway* *job* ?-**groupingKey** *labelsDict*? ?-**timeout** *timeoutMS*?\n\nGiven a *gateway* hostname, *job* value and an optional [`dict`](https://www.tcl.tk/man/tcl8.6/TclCmd/dict.htm) of labels, *labelsDict*, send an HTTP(s) `PUT` (**prom::push_to_gateway**), `POST` (**prom::pushadd_to_gateway**) or `DELETE` (**prom::delete_from_gateway**) to the PushGateway.\n\nThe *gateway* hostname should be the PushGateway's domain name or IP address.  It can optionally include a URI scheme of `http` or `https` (`http` is assumed if none is specified) along with a `:` and port number (`9091` is assumed as the port if none is specified).  \n\nOn success, which means the PushGateway provided a status code indicating such, the **`prom::*_gateway`** procs return `1` or `0` otherwise.  \n\nThese procs will throw an error if the *gateway* is not provided in an acceptable format. \n\n#### Gateway Hostnames\n\nTo make clear the accepted *gateway* values in the above procs, consider an imagined host `gateway.com` running a PushGateway on port `12345`:\n\n```\n# Since no scheme provided, http used by default\n# If no port is provided, 9091 is assumed by default\ngateway.com:12345\n\n# Alternate way of writing the above\nhttp://gateway.com:12345\n\n# For TLS, must explicitly specify https \nhttps://gateway.com:12345\n```\n\nIf an `https` scheme is provided, `tcltls` is used to encrypt the connection to the gateway.\n\n## Multi-threaded Setting\n\n### Collection Policy\n\n`prometheus-tcl` supports a multi-threaded mode of operation.  This is off by default but can be enabled by setting a multi-threaded collection policy of `mt` using the proc\n\n**prom::set_collection_policy** *policy*\n\nBy default the *policy* is `st`, or single-threaded.  That means that `prom::collect` only collects metrics from the current Tcl interpreter.  \n\nIf a *policy* of `mt` is specified, then `prom::collect` will aggregate metrics from [all threads specified for collection](#collect-from-some-but-not-all-threads) and merge their values (for any given metric name and labels).\n\nThe `mt` policy assumes a Tcl program using [threads](https://www.tcl.tk/man/tcl/ThreadCmd/thread.htm) in the context of [the Tcl threading model](https://www.tcl.tk/doc/howto/thread_model.html).  In a multi-threaded application, it is possible that each thread could expose its metrics on a separate port.  However, this has several drawbacks, one of which could be the creation of too many values for the Prometheus-added `instance` label.  It is also likely that each thread will share some (if not all) of the same metric names.  In that case, it is desirable for `prom::expose` to return merged metrics across [all threads with metrics to share](#collect-from-some-but-not-all-threads).  That is the multi-threaded collection policy supported by `prometheus-tcl`. \n\nNote that in the multi-threaded context, `prometheus-tcl` does not have safeguards against multiple threads using the same metric name but different label keys.  If that occurs, the behavior is undefined.  It is up to application developers to enforce this, preferably by doing metric creation in a proc shared by all threads to enforce uniformity.\n\n### Aside On Other Architectures\n\nIn passing it is worth mentioning that it would be possible to avoid merging metrics by creating, for instance, a [cpptcl](https://github.com/flightaware/cpptcl) extension or [tsv](https://www.tcl.tk/man/tcl/ThreadCmd/tsv.htm)s for metric instrumentation in a multi-threaded setting.  Either one of those could be fine solutions and were considered, but are not used in this package. \n\n### Multi-threaded Collection Timeout \n\nWhen collecting metrics in a multi-threaded setting it is possible that one of the threads could block indefinitely and stall the thread that called `prom::collect`.  To avoid this situation, `prometheus-tcl` uses `thread::send -async` along with an after event timeout and `vwait` to set an upper bound on how long collection can take before giving up.  \n\nBy default a collection timeout of `10` milliseconds is used, but, depending on workload and number of threads, this could use adjustment.  To set a different value in milliseconds use\n\n**prom::set_mt_collection_timeout** *timeoutMS*\n\nTo see the current collection timeout value\n\n**prom::get_mt_collection_timeout**\n\nThe timeout value is for the time taken to collect from all threads specified for collection not for an individual thread.\n\nIf some threads fail to return a value before the timeout no error will result.  Any collected metrics will be exposed.\n\n### Collect From Some But Not All Threads\n\nBy default, `prometheus-tcl`'s multi-threaded collection policy collects from all threads returned by `thread::names`.  To set only some threads for collection use\n\n**prom::set_mt_collection_threads** *threadIDs*\n\n### Merge Policy\n\nWhen merging metrics the following rules are followed:\n\n- Metrics with the same name and labels (includes keys and values) can be merged\n- If threads provide different label keys for the same metric name, the result is undefined\n- Counters have their values added together \n- Gauges by default take the maximum value seen.  However, this can be modified with the -**mergePolicy** option for `prom::gauge::new`.  Supported merge policies are `max`, `min`, and `sum`\n- Histograms have the count for each bucket and the overall count and total summed \n- Summaries have their count and total values summed\n- For metrics with timestamps (this is controlled at metric creation time), typically the maximum timestamp seen is taken.  This is not the case, however, for gauges where it depends on the merge policy.  A `sum` policy uses the maximum timestamp seen but `max` or `min` use the timestamp of the maximum or minimum value seen\n\n## Compliance with Prometheus [Client Guidelines](https://prometheus.io/docs/instrumenting/writing_clientlibs/)\n\n`prometheus-tcl` complies with most if not all of the client library recommendations.\n\nFor instance:\n\n- It is thread safe for Tcl's threading model\n- It offers Counter and Gauge and both Summary and Histogram\n- It has a default registry (the client API hides this detail from the user)\n- Using the different classes defined in the `prom` namespace, it is possible to use a different registry\n- All metrics have the mandatory methods and names (except for `prom::gauge::set_value` to avoid collision with `::set`)\n- Unit tests are included (can run them with the **`test`** make target)\n\n## Further Documentation\n\nIf you would like additional documentation on the package and have [Doxygen](http://www.doxygen.nl) installed, a `docs` Make target will run `doxygen`.  This will generate output in the `docs/` sub-directory of the current directory. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflightaware%2Fprometheus-tcl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fflightaware%2Fprometheus-tcl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflightaware%2Fprometheus-tcl/lists"}