├── .gitignore ├── .Rhistory ├── site ├── assets │ ├── javascripts │ │ └── lunr │ │ │ └── min │ │ │ ├── lunr.jp.min.js │ │ │ ├── lunr.vi.min.js │ │ │ ├── lunr.multi.min.js │ │ │ ├── lunr.ja.min.js │ │ │ ├── lunr.stemmer.support.min.js │ │ │ ├── lunr.sv.min.js │ │ │ ├── lunr.da.min.js │ │ │ ├── lunr.no.min.js │ │ │ ├── lunr.nl.min.js │ │ │ ├── lunr.de.min.js │ │ │ ├── lunr.du.min.js │ │ │ ├── lunr.ru.min.js │ │ │ ├── lunr.fi.min.js │ │ │ ├── lunr.hu.min.js │ │ │ ├── lunr.pt.min.js │ │ │ ├── lunr.fr.min.js │ │ │ └── lunr.ro.min.js │ └── images │ │ └── favicon.png ├── images │ ├── titan.png │ ├── shiny-basic.png │ ├── titan-banner.png │ └── shiny-histogram.png ├── sitemap.xml.gz ├── css │ └── extra.css └── sitemap.xml ├── tests ├── testthat.R ├── utils.R └── testthat │ ├── test-type.R │ ├── test-gauge.R │ ├── test-labels.R │ ├── test-counter.R │ ├── test-ns.R │ ├── test-summary.R │ └── test-histogram.R ├── docs ├── images │ ├── titan.png │ ├── shiny-basic.png │ ├── titan-banner.png │ └── shiny-histogram.png ├── css │ └── extra.css ├── meta │ ├── changelog.md │ └── contribute.md ├── guide │ ├── installation.md │ ├── ambiorix.md │ ├── plumber.md │ ├── authentication.md │ ├── shiny.md │ └── metrics.md ├── deploy │ ├── grafana.md │ ├── management.md │ └── prometheus.md ├── index.md └── about │ ├── internals.md │ └── monitoring.md ├── .Rbuildignore ├── NEWS.md ├── .travis.yml ├── man ├── cleanRegistry.Rd ├── previewMetrics.Rd ├── titan.Rd ├── renderMetrics.Rd ├── bucket.Rd ├── getMetrics.Rd ├── ns.Rd ├── prTitan.Rd ├── auth.Rd ├── titanApp.Rd ├── mgmt.Rd ├── Summary.Rd ├── Histogram.Rd ├── Binned.Rd ├── Gauge.Rd ├── Counter.Rd └── MetricInterface.Rd ├── R ├── utils.R ├── ambiorix.R ├── error.R ├── gauge.R ├── buckets.R ├── meta.R ├── counter.R ├── handlers.R ├── registry.R ├── plumber.R ├── auth.R ├── management.R ├── binned.R ├── shiny.R └── metrics.R ├── inst └── examples │ └── plumber.R ├── NAMESPACE ├── DESCRIPTION ├── appveyor.yml ├── mkdocs.yml ├── README.md └── CODE_OF_CONDUCT.md /.gitignore: -------------------------------------------------------------------------------- 1 | test.R 2 | -------------------------------------------------------------------------------- /.Rhistory: -------------------------------------------------------------------------------- 1 | 130 * 18 2 | q() 3 | -------------------------------------------------------------------------------- /site/assets/javascripts/lunr/min/lunr.jp.min.js: -------------------------------------------------------------------------------- 1 | module.exports=require("./lunr.ja"); -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(titan) 3 | 4 | test_check("titan") 5 | -------------------------------------------------------------------------------- /docs/images/titan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devOpifex/titan/HEAD/docs/images/titan.png -------------------------------------------------------------------------------- /site/images/titan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devOpifex/titan/HEAD/site/images/titan.png -------------------------------------------------------------------------------- /site/sitemap.xml.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devOpifex/titan/HEAD/site/sitemap.xml.gz -------------------------------------------------------------------------------- /docs/images/shiny-basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devOpifex/titan/HEAD/docs/images/shiny-basic.png -------------------------------------------------------------------------------- /docs/images/titan-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devOpifex/titan/HEAD/docs/images/titan-banner.png -------------------------------------------------------------------------------- /site/images/shiny-basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devOpifex/titan/HEAD/site/images/shiny-basic.png -------------------------------------------------------------------------------- /site/images/titan-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devOpifex/titan/HEAD/site/images/titan-banner.png -------------------------------------------------------------------------------- /docs/images/shiny-histogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devOpifex/titan/HEAD/docs/images/shiny-histogram.png -------------------------------------------------------------------------------- /site/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devOpifex/titan/HEAD/site/assets/images/favicon.png -------------------------------------------------------------------------------- /site/images/shiny-histogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devOpifex/titan/HEAD/site/images/shiny-histogram.png -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^test\.R$ 2 | ^LICENSE\.md$ 3 | ^\.github$ 4 | ^\.travis\.yml$ 5 | ^appveyor\.yml$ 6 | mkdocs.yml 7 | docs 8 | site 9 | ^CODE_OF_CONDUCT\.md$ 10 | -------------------------------------------------------------------------------- /tests/utils.R: -------------------------------------------------------------------------------- 1 | getStorage <- function(name){ 2 | items <- ls(titan:::titanCollector) 3 | for(item in items){ 4 | metric <- titan:::titanCollector[[item]] 5 | 6 | if(metric$getName() == name) 7 | return(metric) 8 | } 9 | 10 | return("ERROR") 11 | } -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # titan 2.0.1 2 | 3 | - Added ambiorix middleware 4 | 5 | # titan 2.0.0 6 | 7 | - Refactor entire source code. 8 | - Better performances. 9 | - Documentation site. 10 | - Support for authentication. 11 | - Support Management API. 12 | 13 | # titan 1.0.0 14 | 15 | Initial version. 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # R for travis: see documentation at https://docs.travis-ci.com/user/languages/r 2 | 3 | language: R 4 | cache: packages 5 | 6 | before_install: 7 | - sudo apt-get update 8 | - sudo apt-get install -y libsodium-dev 9 | 10 | after_success: 11 | - Rscript -e 'covr::coveralls()' 12 | -------------------------------------------------------------------------------- /man/cleanRegistry.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/registry.R 3 | \name{cleanRegistry} 4 | \alias{cleanRegistry} 5 | \title{Clean Registry} 6 | \usage{ 7 | cleanRegistry() 8 | } 9 | \description{ 10 | Empties the registry from all metrics. 11 | } 12 | -------------------------------------------------------------------------------- /man/previewMetrics.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/registry.R 3 | \name{previewMetrics} 4 | \alias{previewMetrics} 5 | \title{Preview Metrics} 6 | \usage{ 7 | previewMetrics() 8 | } 9 | \description{ 10 | Preview the metrics as are served by titan. 11 | } 12 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | `%||%` <- function(lhs, rhs){ 2 | if(is.null(lhs)) 3 | return(rhs) 4 | 5 | lhs 6 | } 7 | 8 | checkInstalled <- function(pkg){ 9 | has_it <- base::requireNamespace(pkg, quietly = TRUE) 10 | 11 | if(!has_it) 12 | stop(sprintf("This function requires the package {%s}", pkg), call. = FALSE) 13 | } 14 | -------------------------------------------------------------------------------- /docs/css/extra.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300&family=Raleway:wght@300;700&display=swap'); 2 | 3 | body, input { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | code, pre { 8 | font-family: 'JetBrains Mono', monospace; 9 | } 10 | 11 | .wy-side-nav-search{ 12 | background-color: #e6522c; 13 | } 14 | -------------------------------------------------------------------------------- /site/css/extra.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300&family=Raleway:wght@300;700&display=swap'); 2 | 3 | body, input { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | code, pre { 8 | font-family: 'JetBrains Mono', monospace; 9 | } 10 | 11 | .wy-side-nav-search{ 12 | background-color: #e6522c; 13 | } 14 | -------------------------------------------------------------------------------- /man/titan.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ambiorix.R 3 | \name{titan} 4 | \alias{titan} 5 | \title{Ambiorix Middleware} 6 | \usage{ 7 | titan(path = "metrics") 8 | } 9 | \arguments{ 10 | \item{path}{Path on which to serve the metrics. 11 | Default recommended.} 12 | } 13 | \description{ 14 | Middleware for ambiorix. 15 | } 16 | -------------------------------------------------------------------------------- /docs/meta/changelog.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Changelog 3 | summary: Changes in the source code and features. 4 | authors: 5 | - John Coene 6 | --- 7 | 8 | # titan 2.0.0 9 | 10 | - Refactor entire source code. 11 | - Better performances. 12 | - Documentation site. 13 | - Support for authentication. 14 | - Support Management API. 15 | 16 | # titan 1.0.0 17 | 18 | Initial version. 19 | -------------------------------------------------------------------------------- /R/ambiorix.R: -------------------------------------------------------------------------------- 1 | #' Ambiorix Middleware 2 | #' 3 | #' Middleware for ambiorix. 4 | #' 5 | #' @param path Path on which to serve the metrics. 6 | #' Default recommended. 7 | #' 8 | #' @export 9 | titan <- function( 10 | path = "metrics" 11 | ) { 12 | function(req, res) { 13 | if(!req$PATH_INFO == "/metrics") 14 | return() 15 | 16 | res$test(renderMetrics()) 17 | } 18 | } -------------------------------------------------------------------------------- /man/renderMetrics.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/registry.R 3 | \name{renderMetrics} 4 | \alias{renderMetrics} 5 | \title{Render All Metrics} 6 | \usage{ 7 | renderMetrics() 8 | } 9 | \description{ 10 | Renders all metrics from the registry. 11 | } 12 | \seealso{ 13 | \code{\link[=previewMetrics]{previewMetrics()}} for a more readable 14 | output. 15 | } 16 | -------------------------------------------------------------------------------- /tests/testthat/test-type.R: -------------------------------------------------------------------------------- 1 | test_that("Type", { 2 | cleanRegistry() 3 | 4 | # init 5 | cnter <- Counter$new("btn_total", "total clicks") 6 | cnter$inc() 7 | x <- cnter$get() 8 | 9 | # don't override 10 | cnter <- Counter$new("btn_total", "total clicks") 11 | y <- cnter$get() 12 | 13 | expect_equal(x, y) 14 | 15 | # fails 16 | expect_error(Gauge$new("btn_total", "total clicks")) 17 | 18 | }) 19 | -------------------------------------------------------------------------------- /R/error.R: -------------------------------------------------------------------------------- 1 | # escalate errors 2 | # allows avoiding running `stop` 3 | # would be too ironic that prometheus breaks a service 4 | # used here while {err} devOpifex/err is not on CRAN 5 | Error <- function(obj = ""){ 6 | attr(obj, "error") <- TRUE 7 | return(obj) 8 | } 9 | 10 | is.error <- function(obj){ 11 | iserror <- attr(obj, "error") 12 | 13 | if(is.null(iserror)) 14 | return(FALSE) 15 | 16 | return(TRUE) 17 | } -------------------------------------------------------------------------------- /man/bucket.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/buckets.R 3 | \name{bucket} 4 | \alias{bucket} 5 | \alias{is.bucket} 6 | \title{Bucket} 7 | \usage{ 8 | bucket(label, value) 9 | 10 | is.bucket(obj) 11 | } 12 | \arguments{ 13 | \item{label}{Label of bucket.} 14 | 15 | \item{value}{Value of the bucket.} 16 | 17 | \item{obj}{Object to check.} 18 | } 19 | \description{ 20 | Create buckets as must be returned by the 21 | \link{Histogram} and \link{Summary} predicates. 22 | } 23 | -------------------------------------------------------------------------------- /R/gauge.R: -------------------------------------------------------------------------------- 1 | #' Gauge 2 | #' 3 | #' @export 4 | Gauge <- R6::R6Class( 5 | "Gauge", 6 | inherit = MetricInterface, 7 | public = list( 8 | #' @details Initialise 9 | #' 10 | #' @param name Name of the metric. 11 | #' @param help Help text describing the metric. 12 | #' @param labels Character vector of labels available. 13 | #' @param unit Unit of metric. 14 | initialize = function(name, help, labels = NULL, unit = NULL){ 15 | super$initialize(name, help, labels = labels, unit = unit, type = "gauge") 16 | } 17 | ) 18 | ) 19 | -------------------------------------------------------------------------------- /inst/examples/plumber.R: -------------------------------------------------------------------------------- 1 | library(titan) 2 | 3 | cnter <- Counter$new( 4 | "home", 5 | "Counts number of pings at /", 6 | labels = "endpoint" 7 | ) 8 | 9 | #* Increment a counter 10 | #* @get / 11 | function() { 12 | cnter$inc(endpoint = "home") 13 | return("Hello titan!") 14 | } 15 | 16 | #* Increment the same counter 17 | #* @get /count 18 | function() { 19 | cnter$inc(endpoint = "count") 20 | return("More titan!") 21 | } 22 | 23 | #* Render Metrics 24 | #* 25 | #* @serializer text 26 | #* @get /metrics 27 | renderMetrics 28 | -------------------------------------------------------------------------------- /man/getMetrics.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/auth.R 3 | \name{getMetrics} 4 | \alias{getMetrics} 5 | \title{Retrieve Metrics from Endpoint} 6 | \usage{ 7 | getMetrics(endpoint, auth = NULL) 8 | } 9 | \arguments{ 10 | \item{endpoint}{URL to the endpoint.} 11 | 12 | \item{auth}{Authentication string as returned 13 | by \code{\link[=generateBasicAuth]{generateBasicAuth()}}.} 14 | } 15 | \description{ 16 | Retrieve the metrics via the endpoint, 17 | useful for testing authentication. 18 | } 19 | -------------------------------------------------------------------------------- /tests/testthat/test-gauge.R: -------------------------------------------------------------------------------- 1 | test_that("Gauge", { 2 | cleanRegistry() 3 | 4 | expect_error(Gauge$new()) 5 | expect_error(Gauge$new("name")) 6 | 7 | g <- Gauge$new("btn_clicks", "total clicks") 8 | g$inc() 9 | expect_equal(g$get(), 1) 10 | g$dec() 11 | expect_equal(g$get(), 0) 12 | 13 | g$set(3) 14 | expect_equal(g$get(), 3) 15 | g$set(2) 16 | expect_equal(g$get(), 2) 17 | 18 | expect_equal( 19 | renderMetrics(), 20 | "# HELP btn_clicks total clicks\n# TYPE btn_clicks gauge\nbtn_clicks 2\n" 21 | ) 22 | }) 23 | -------------------------------------------------------------------------------- /tests/testthat/test-labels.R: -------------------------------------------------------------------------------- 1 | test_that("Labels", { 2 | cleanRegistry() 3 | 4 | cnter <- Counter$new("btn_total", "total clicks", labels = "color") 5 | expect_warning(cnter$inc()) 6 | expect_equal(cnter$get(color = "red"), 0) 7 | expect_warning(cnter$dec()) 8 | expect_warning(cnter$inc(wrong = "hello")) 9 | 10 | cnter$inc(1, color = "blue") 11 | expect_equal(cnter$get(color = "blue"), 1) 12 | 13 | expect_equal( 14 | renderMetrics(), 15 | "# HELP btn_total total clicks\n# TYPE btn_total counter\nbtn_total{color=\"blue\"} 1\n" 16 | ) 17 | }) 18 | -------------------------------------------------------------------------------- /man/ns.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/registry.R 3 | \name{ns} 4 | \alias{ns} 5 | \alias{setTitanNamespace} 6 | \alias{getTitanNamespace} 7 | \title{Set a Namespace} 8 | \usage{ 9 | setTitanNamespace(name = NULL) 10 | 11 | getTitanNamespace() 12 | } 13 | \arguments{ 14 | \item{name}{Namespace.} 15 | } 16 | \description{ 17 | Set a namespace, all metrics name will 18 | be preceded by the namespace and an underscore: 19 | \code{namespace_metricName}. 20 | } 21 | \note{ 22 | Rerun the function to remove the namespace. 23 | } 24 | -------------------------------------------------------------------------------- /tests/testthat/test-counter.R: -------------------------------------------------------------------------------- 1 | test_that("Counter", { 2 | cleanRegistry() 3 | 4 | expect_error(Counter$new()) 5 | expect_error(Counter$new("name")) 6 | 7 | cnter <- Counter$new("btn_total", "total clicks") 8 | cnter$inc() 9 | expect_equal(cnter$get(), 1) 10 | expect_warning(cnter$dec()) 11 | 12 | cnter$set(3) 13 | expect_equal(cnter$get(), 3) 14 | expect_error(cnter$set(2)) 15 | cnter$set(4) 16 | expect_equal(cnter$get(), 4) 17 | 18 | expect_equal( 19 | renderMetrics(), 20 | "# HELP btn_total total clicks\n# TYPE btn_total counter\nbtn_total 4\n" 21 | ) 22 | 23 | testthat::expect_output(previewMetrics()) 24 | }) 25 | -------------------------------------------------------------------------------- /tests/testthat/test-ns.R: -------------------------------------------------------------------------------- 1 | test_that("Namespace", { 2 | cleanRegistry() 3 | 4 | setTitanNamespace("ns") 5 | 6 | expect_error(Counter$new()) 7 | expect_error(Counter$new("name")) 8 | 9 | cnter <- Counter$new("btn_total", "total clicks") 10 | cnter$inc() 11 | expect_equal(cnter$get(), 1) 12 | expect_warning(cnter$dec()) 13 | 14 | cnter$set(3) 15 | expect_equal(cnter$get(), 3) 16 | expect_error(cnter$set(2)) 17 | cnter$set(4) 18 | expect_equal(cnter$get(), 4) 19 | 20 | expect_equal( 21 | renderMetrics(), 22 | "# HELP ns_btn_total total clicks\n# TYPE ns_btn_total counter\nns_btn_total 4\n" 23 | ) 24 | 25 | # reset 26 | setTitanNamespace() 27 | }) 28 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(print,bucket) 4 | export(Binned) 5 | export(Counter) 6 | export(Gauge) 7 | export(Histogram) 8 | export(MetricInterface) 9 | export(PromHealthy) 10 | export(PromQuit) 11 | export(PromReady) 12 | export(PromReload) 13 | export(Summary) 14 | export(bucket) 15 | export(cleanRegistry) 16 | export(generateBasicAuth) 17 | export(getAuthentication) 18 | export(getMetrics) 19 | export(getTitanNamespace) 20 | export(is.bucket) 21 | export(prTitan) 22 | export(previewMetrics) 23 | export(renderMetrics) 24 | export(setAuthentication) 25 | export(setTitanNamespace) 26 | export(titan) 27 | export(titanApp) 28 | importFrom(utils,getFromNamespace) 29 | -------------------------------------------------------------------------------- /man/prTitan.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plumber.R 3 | \name{prTitan} 4 | \alias{prTitan} 5 | \title{Plumber} 6 | \usage{ 7 | prTitan(file = NULL, ..., latency = NULL) 8 | } 9 | \arguments{ 10 | \item{file}{Plumber file as passed to \code{\link[plumber:pr]{plumber::pr()}}.} 11 | 12 | \item{...}{Any other argument to pass to \code{\link[plumber:pr]{plumber::pr()}}.} 13 | 14 | \item{latency}{Pass a character string to use as metric name 15 | to track request latency. Leave on \code{NULL} to not track.} 16 | } 17 | \description{ 18 | Serve a plumber API with metrics, use this function 19 | like you would use \code{\link[plumber:pr]{plumber::pr()}}. 20 | } 21 | -------------------------------------------------------------------------------- /R/buckets.R: -------------------------------------------------------------------------------- 1 | #' Bucket 2 | #' 3 | #' Create buckets as must be returned by the 4 | #' [Histogram] and [Summary] predicates. 5 | #' 6 | #' @param obj Object to check. 7 | #' @param label Label of bucket. 8 | #' @param value Value of the bucket. 9 | #' 10 | #' @name bucket 11 | #' 12 | #' @export 13 | bucket <- function(label, value){ 14 | label <- as.character(label) 15 | b <- list(label = label, value = value) 16 | structure(b, class = c("bucket", class(b))) 17 | } 18 | 19 | #' @rdname bucket 20 | #' @export 21 | is.bucket <- function(obj){ 22 | if(inherits(obj, "bucket")) 23 | return(TRUE) 24 | 25 | FALSE 26 | } 27 | 28 | #' @export 29 | print.bucket <- function(x, ...){ 30 | cat("Bucket\nlabel:", x$label, "\nvalue:", x$value) 31 | } 32 | -------------------------------------------------------------------------------- /site/assets/javascripts/lunr/min/lunr.vi.min.js: -------------------------------------------------------------------------------- 1 | !function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.vi=function(){this.pipeline.reset(),this.pipeline.add(e.vi.stopWordFilter,e.vi.trimmer)},e.vi.wordCharacters="[A-Za-ẓ̀͐́͑̉̃̓ÂâÊêÔôĂ-ăĐ-đƠ-ơƯ-ư]",e.vi.trimmer=e.trimmerSupport.generateTrimmer(e.vi.wordCharacters),e.Pipeline.registerFunction(e.vi.trimmer,"trimmer-vi"),e.vi.stopWordFilter=e.generateStopWordFilter("là cái nhưng mà".split(" "))}}); -------------------------------------------------------------------------------- /site/assets/javascripts/lunr/min/lunr.multi.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(e.lunr)}(this,function(){return function(e){e.multiLanguage=function(){for(var t=Array.prototype.slice.call(arguments),i=t.join("-"),r="",n=[],s=[],p=0;p 4 | [![Travis build status](https://travis-ci.com/devOpifex/titan.svg?branch=master)](https://travis-ci.com/devOpifex/titan) 5 | [![AppVeyor build status](https://ci.appveyor.com/api/projects/status/github/devOpifex/titan?branch=master&svg=true)](https://ci.appveyor.com/project/devOpifex/titan) 6 | [![Coveralls test coverage](https://coveralls.io/repos/github/devOpifex/titan/badge.svg)](https://coveralls.io/r/devOpifex/titan?branch=master) 7 | 8 | 9 | [Prometheus](prometheus.io/) monitoring for shiny applications, plumber APIs, and other R web services. 10 | 11 | [Monitoring](/about/monitoring){: .md-button .md-button--primary } 12 | [Get Started](/guide/installation){: .md-button } 13 | 14 | ## Acknowledgement 15 | 16 | I have put this package together in order to 1) grasp a better understanding of Prometheus metrics and 2) have some direct control over the source code of software I deploy for clients. I have written and re-written this three times before discovering [openmetrics](https://github.com/atheriel/openmetrics/), an R package that provides the same functionalities. I have taken much inspiration from it. 17 | 18 | !!! info 19 | Prometheus is the _titan_ god of fire. 20 | 21 | ## Related work 22 | 23 | There are other packages out there that will let you serve Prometheus metrics. 24 | 25 | - [openmetrics](https://github.com/atheriel/openmetrics/) provides support for all metrics as well as authentication, and goes a step further in enforcing [OpenMetrics](https://openmetrics.io/) standards. 26 | - [pRometheus](https://github.com/cfmack/pRometheus/) Provides support for Gauge and Counter. 27 | -------------------------------------------------------------------------------- /docs/guide/plumber.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Using titan with plumber 3 | summary: How to use titan with plumber APIs. 4 | authors: 5 | - John Coene 6 | --- 7 | 8 | # Plumber APIs 9 | 10 | The metrics and how they are used never changes, the only thing that changes across projects is how the metrics are served. 11 | 12 | Create the plumber API as you normally would. 13 | 14 | ```r 15 | #* Increment a counter 16 | #* @get / 17 | function() { 18 | return("Hello titan!") 19 | } 20 | 21 | #* Plot a histogram 22 | #* @serializer png 23 | #* @get /plot 24 | function() { 25 | rand <- rnorm(100) 26 | hist(rand) 27 | } 28 | ``` 29 | 30 | Then use the function `prTitan` as you would normally use `pr`. This example here will serve the metrics but _none are currently tracked._ 31 | 32 | ```r hl_lines="3" 33 | library(plumber) 34 | 35 | titan::prTitan("file.R") %>% 36 | pr_run() 37 | ``` 38 | 39 | As with shiny, titan provides an out-of-the-box metric to track: `latency`. As with shiny (`titanApp`), the `latency` argument defaults to `NULL` meaning this is not being tracked, to turn on that tracking pass it a character string: the name of the metric. 40 | 41 | Latency tracks the time it takes for the API to serve requests. This is tracked with a Histogram that uses predefined `bucket`s that put the request time in milliseconds in various bins (e.g.: 300 milliseconds, 600 milliseconds, etc.). It also uses some labels to track: 42 | 43 | 1. The method used for the request, e.g.: `GET` or `POST`. 44 | 2. The path of the request, e.g.: `/plot`. 45 | 3. The status of the request, e.g.: `200` or `404`. 46 | 47 | ```r 48 | library(plumber) 49 | 50 | titan::prTitan("file.R", latency = "latency") %>% 51 | pr_run() 52 | ``` 53 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: titan 2 | theme: 3 | name: material 4 | logo: images/titan.png 5 | favicon: images/titan.png 6 | use_pygments: true 7 | palette: 8 | primary: deep orange 9 | accent: orange 10 | features: 11 | - navigation.instant 12 | - search.suggest 13 | 14 | plugins: 15 | - search: 16 | min_search_length: 2 17 | 18 | extra_css: 19 | - css/extra.css 20 | 21 | extra: 22 | social: 23 | - icon: fontawesome/brands/twitter 24 | link: https://twitter.com/devOpifex 25 | - icon: fontawesome/brands/github 26 | link: https://github.com/devOpifex 27 | 28 | repo_url: https://github.com/devOpifex/titan/ 29 | repo_name: Github 30 | site_description: Serve Prometheus metrics for shiny applications and plumber services 31 | site_author: Opifex 32 | copyright: Copyright © 2020 Opifex 33 | google_analytics: ['G-TZMQ98HS6D', 'titan.opifex.org'] 34 | 35 | markdown_extensions: 36 | - admonition 37 | - meta 38 | - smarty 39 | - pymdownx.highlight: 40 | linenums: true 41 | - pymdownx.superfences 42 | - pymdownx.inlinehilite 43 | - attr_list 44 | - toc: 45 | permalink: True 46 | separator: "-" 47 | 48 | nav: 49 | - Home: index.md 50 | - About: 51 | - Monitoring: about/monitoring.md 52 | - Internals: about/internals.md 53 | - Guide: 54 | - Installation: guide/installation.md 55 | - Metrics: guide/metrics.md 56 | - Shiny: guide/shiny.md 57 | - Plumber: guide/plumber.md 58 | - Ambiorix: guide/ambiorix.md 59 | - Authentication: guide/authentication.md 60 | - Deploy: 61 | - Prometheus: deploy/prometheus.md 62 | - Management: deploy/management.md 63 | - Grafana: deploy/grafana.md 64 | - Contribute: meta/contribute.md 65 | - Changelog: meta/changelog.md 66 | - License: meta/license.md 67 | -------------------------------------------------------------------------------- /docs/about/internals.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Internals of titan 3 | summary: Some description of the internals of the package. 4 | authors: 5 | - John Coene 6 | --- 7 | 8 | # Internals 9 | 10 | This gives some details on the internals of titan that may be of use when using the package. 11 | 12 | ## Principle 13 | 14 | Titan aims not to make your service go down, the irony that would be... Therefore titan makes very little use of `stop` and instead uses `warning` starting in `[IGNORING]` and skips the action. 15 | 16 | !!! tip 17 | Warnings starting in `[IGNORING]` indicate actions being ignored, e.g.: metric not being set to given value. 18 | 19 | E.g.: One cannot decrease the value of Counters, if you try to do so Titan will return a warning and ignore the action but will not `stop`. 20 | 21 | ## Storage 22 | 23 | When titan is loaded in your environment with `library(titan)` it creates an environment in which it stores all metrics. 24 | 25 | This is useful when, for instance, you setup a shiny application with titan, run it locally with `titanApp`, test it, then stop the app to make changes; when you run it again the metrics from the previous run will remain. 26 | 27 | !!! info 28 | You can run `cleanRegistry` to clean the environment. 29 | 30 | ## Serving 31 | 32 | As mentioned Prometheus pulls the metrics from your service, therefore those metrics have to be made available. These are exposed in the form of a plain text (`text/plain`) endpoint called `/metrics` where titan will serve the metrics that Prometheus can then read, store, and analyse. 33 | 34 | With shiny you can use the function `titanApp` where you would normally use `shinyApp`, with plumber you can use `prTitan` where you would normally use `pr`, with Ambiorix, and other services you can use `renderMetrics`. 35 | 36 | There are more details and many examples on how to use this on this site. 37 | -------------------------------------------------------------------------------- /man/mgmt.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/management.R 3 | \name{mgmt} 4 | \alias{mgmt} 5 | \alias{PromReload} 6 | \alias{PromHealthy} 7 | \alias{PromReady} 8 | \alias{PromQuit} 9 | \title{Management} 10 | \usage{ 11 | PromReload(uri = Sys.getenv("PROMETHEUS_URL", "http://localhost:9090")) 12 | 13 | PromHealthy(uri = Sys.getenv("PROMETHEUS_URL", "http://localhost:9090")) 14 | 15 | PromReady(uri = Sys.getenv("PROMETHEUS_URL", "http://localhost:9090")) 16 | 17 | PromQuit( 18 | uri = Sys.getenv("PROMETHEUS_URL", "http://localhost:9090"), 19 | prompt = TRUE 20 | ) 21 | } 22 | \arguments{ 23 | \item{uri}{URL to the prometheus service, including 24 | the port if necessary. It defaults to the \code{PROMETHEUS_URL} 25 | environment variable and if this is not set uses 26 | \verb{http://localhost:9090}.} 27 | 28 | \item{prompt}{Whether to prompt the user for an input, 29 | used for the dangerous \code{PromQuit} which shuts down the 30 | Prometheus server. Defaults to \code{TRUE}.} 31 | } 32 | \description{ 33 | Interact with the management API of Prometheus. 34 | } 35 | \note{ 36 | All management-related functions start with a capital 37 | letter. 38 | } 39 | \section{Functions}{ 40 | 41 | \itemize{ 42 | \item \code{PromReload}: Tell Prometheus to reload the configuration file. 43 | Note that this only works if Prometheus was launched 44 | with the \code{--web.enable-lifecycle} flag. 45 | \item \code{PromReady}: Check whether Prometeus is ready to serve traffic 46 | (i.e. respond to queries). Returns \code{TRUE} if Prometheus is ready. 47 | \item \code{PromHealthy}: Checks Prometheus' health. 48 | Returns \code{TRUE} if Prometheus is healthy. 49 | \item \code{PromQuit}: riggers a graceful shutdown of Prometheus. 50 | It's disabled by default and can be enabled via the 51 | \code{--web.enable-lifecycle} flag. 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /site/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | None 4 | 2020-12-19 5 | daily 6 | 7 | None 8 | 2020-12-19 9 | daily 10 | 11 | None 12 | 2020-12-19 13 | daily 14 | 15 | None 16 | 2020-12-19 17 | daily 18 | 19 | None 20 | 2020-12-19 21 | daily 22 | 23 | None 24 | 2020-12-19 25 | daily 26 | 27 | None 28 | 2020-12-19 29 | daily 30 | 31 | None 32 | 2020-12-19 33 | daily 34 | 35 | None 36 | 2020-12-19 37 | daily 38 | 39 | None 40 | 2020-12-19 41 | daily 42 | 43 | None 44 | 2020-12-19 45 | daily 46 | 47 | None 48 | 2020-12-19 49 | daily 50 | 51 | None 52 | 2020-12-19 53 | daily 54 | 55 | None 56 | 2020-12-19 57 | daily 58 | 59 | None 60 | 2020-12-19 61 | daily 62 | 63 | -------------------------------------------------------------------------------- /R/registry.R: -------------------------------------------------------------------------------- 1 | # global registry 2 | .registry <- new.env(hash = TRUE) 3 | 4 | #' Registry Interface 5 | #' 6 | #' Light interface to the registry. 7 | #' 8 | #' @noRd 9 | #' @keywords internal 10 | registrySet <- function(name, obj){ 11 | .registry[[name]] <- obj 12 | } 13 | 14 | #' @noRd 15 | #' @keywords internal 16 | registryGet <- function(name){ 17 | .registry[[name]] 18 | } 19 | 20 | #' @noRd 21 | #' @keywords internal 22 | registryRmv <- function(name){ 23 | rm(name, envir = .registry) 24 | } 25 | 26 | #' @noRd 27 | #' @keywords internal 28 | registryCln <- function(){ 29 | rm(list = ls(.registry), envir = .registry) 30 | } 31 | 32 | #' Render All Metrics 33 | #' 34 | #' Renders all metrics from the registry. 35 | #' 36 | #' @seealso [previewMetrics()] for a more readable 37 | #' output. 38 | #' 39 | #' @export 40 | renderMetrics <- function(){ 41 | metricNames <- ls(.registry) 42 | 43 | metricsRendered <- lapply(metricNames, function(m){ 44 | .registry[[m]]$render() 45 | }) 46 | 47 | paste0(metricsRendered, collapse = "") 48 | 49 | } 50 | 51 | #' Preview Metrics 52 | #' 53 | #' Preview the metrics as are served by titan. 54 | #' 55 | #' @export 56 | previewMetrics <- function(){ 57 | cat(renderMetrics()) 58 | } 59 | 60 | #' Clean Registry 61 | #' 62 | #' Empties the registry from all metrics. 63 | #' 64 | #' @export 65 | cleanRegistry <- function(){ 66 | registryCln() 67 | options(TITAN_NAMESPACE = NULL) 68 | options(TITAN_BASIC_AUTH = NULL) 69 | } 70 | 71 | #' Set a Namespace 72 | #' 73 | #' Set a namespace, all metrics name will 74 | #' be preceded by the namespace and an underscore: 75 | #' `namespace_metricName`. 76 | #' 77 | #' @param name Namespace. 78 | #' 79 | #' @note Rerun the function to remove the namespace. 80 | #' 81 | #' @name ns 82 | #' 83 | #' @export 84 | setTitanNamespace <- function(name = NULL){ 85 | options(TITAN_NAMESPACE = name) 86 | } 87 | 88 | #' @rdname ns 89 | #' @export 90 | getTitanNamespace <- function(){ 91 | getOption("TITAN_NAMESPACE", NULL) 92 | } -------------------------------------------------------------------------------- /site/assets/javascripts/lunr/min/lunr.ja.min.js: -------------------------------------------------------------------------------- 1 | !function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r="2"==e.version[0];e.ja=function(){this.pipeline.reset(),this.pipeline.add(e.ja.trimmer,e.ja.stopWordFilter,e.ja.stemmer),r?this.tokenizer=e.ja.tokenizer:(e.tokenizer&&(e.tokenizer=e.ja.tokenizer),this.tokenizerFn&&(this.tokenizerFn=e.ja.tokenizer))};var t=new e.TinySegmenter;e.ja.tokenizer=function(i){var n,o,s,p,a,u,m,l,c,f;if(!arguments.length||null==i||void 0==i)return[];if(Array.isArray(i))return i.map(function(t){return r?new e.Token(t.toLowerCase()):t.toLowerCase()});for(o=i.toString().toLowerCase().replace(/^\s+/,""),n=o.length-1;n>=0;n--)if(/\S/.test(o.charAt(n))){o=o.substring(0,n+1);break}for(a=[],s=o.length,c=0,l=0;c<=s;c++)if(u=o.charAt(c),m=c-l,u.match(/\s/)||c==s){if(m>0)for(p=t.segment(o.slice(l,c)).filter(function(e){return!!e}),f=l,n=0;n \code{Summary} 21 | } 22 | \section{Methods}{ 23 | \subsection{Public methods}{ 24 | \itemize{ 25 | \item \href{#method-new}{\code{Summary$new()}} 26 | \item \href{#method-clone}{\code{Summary$clone()}} 27 | } 28 | } 29 | \if{html}{ 30 | \out{
Inherited methods} 31 | \itemize{ 32 | \item \out{}\href{../../titan/html/Binned.html#method-observe}{\code{titan::Binned$observe()}}\out{} 33 | } 34 | \out{
} 35 | } 36 | \if{html}{\out{
}} 37 | \if{html}{\out{}} 38 | \if{latex}{\out{\hypertarget{method-new}{}}} 39 | \subsection{Method \code{new()}}{ 40 | \subsection{Usage}{ 41 | \if{html}{\out{
}}\preformatted{Summary$new(name, help, predicate, labels = NULL, unit = NULL)}\if{html}{\out{
}} 42 | } 43 | 44 | \subsection{Arguments}{ 45 | \if{html}{\out{
}} 46 | \describe{ 47 | \item{\code{name}}{Name of the metric.} 48 | 49 | \item{\code{help}}{Help text describing the metric.} 50 | 51 | \item{\code{predicate}}{Predicate function to bin observations.} 52 | 53 | \item{\code{labels}}{Character vector of labels available.} 54 | 55 | \item{\code{unit}}{Unit of metric.} 56 | } 57 | \if{html}{\out{
}} 58 | } 59 | } 60 | \if{html}{\out{
}} 61 | \if{html}{\out{}} 62 | \if{latex}{\out{\hypertarget{method-clone}{}}} 63 | \subsection{Method \code{clone()}}{ 64 | The objects of this class are cloneable with this method. 65 | \subsection{Usage}{ 66 | \if{html}{\out{
}}\preformatted{Summary$clone(deep = FALSE)}\if{html}{\out{
}} 67 | } 68 | 69 | \subsection{Arguments}{ 70 | \if{html}{\out{
}} 71 | \describe{ 72 | \item{\code{deep}}{Whether to make a deep clone.} 73 | } 74 | \if{html}{\out{
}} 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /man/Histogram.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/binned.R 3 | \name{Histogram} 4 | \alias{Histogram} 5 | \title{Histogram} 6 | \description{ 7 | Create a histogram to bin observed metrics. Values 8 | observed at preprocessed with the \code{predicate} function 9 | passed at instantiation. This \code{predicate} function, 10 | \strong{must} return an object of class \code{bucket}, see 11 | \code{\link[=bucket]{bucket()}}. 12 | } 13 | \details{ 14 | While the \link{Histogram} counts up the values in each 15 | bucket, the \link{Summary} sums these up over time. 16 | 17 | Initialise 18 | } 19 | \section{Super class}{ 20 | \code{\link[titan:Binned]{titan::Binned}} -> \code{Histogram} 21 | } 22 | \section{Methods}{ 23 | \subsection{Public methods}{ 24 | \itemize{ 25 | \item \href{#method-new}{\code{Histogram$new()}} 26 | \item \href{#method-clone}{\code{Histogram$clone()}} 27 | } 28 | } 29 | \if{html}{ 30 | \out{
Inherited methods} 31 | \itemize{ 32 | \item \out{}\href{../../titan/html/Binned.html#method-observe}{\code{titan::Binned$observe()}}\out{} 33 | } 34 | \out{
} 35 | } 36 | \if{html}{\out{
}} 37 | \if{html}{\out{}} 38 | \if{latex}{\out{\hypertarget{method-new}{}}} 39 | \subsection{Method \code{new()}}{ 40 | \subsection{Usage}{ 41 | \if{html}{\out{
}}\preformatted{Histogram$new(name, help, predicate, labels = NULL, unit = NULL)}\if{html}{\out{
}} 42 | } 43 | 44 | \subsection{Arguments}{ 45 | \if{html}{\out{
}} 46 | \describe{ 47 | \item{\code{name}}{Name of the metric.} 48 | 49 | \item{\code{help}}{Help text describing the metric.} 50 | 51 | \item{\code{predicate}}{Predicate function to bin observations.} 52 | 53 | \item{\code{labels}}{Character vector of labels available.} 54 | 55 | \item{\code{unit}}{Unit of metric.} 56 | } 57 | \if{html}{\out{
}} 58 | } 59 | } 60 | \if{html}{\out{
}} 61 | \if{html}{\out{}} 62 | \if{latex}{\out{\hypertarget{method-clone}{}}} 63 | \subsection{Method \code{clone()}}{ 64 | The objects of this class are cloneable with this method. 65 | \subsection{Usage}{ 66 | \if{html}{\out{
}}\preformatted{Histogram$clone(deep = FALSE)}\if{html}{\out{
}} 67 | } 68 | 69 | \subsection{Arguments}{ 70 | \if{html}{\out{
}} 71 | \describe{ 72 | \item{\code{deep}}{Whether to make a deep clone.} 73 | } 74 | \if{html}{\out{
}} 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /R/auth.R: -------------------------------------------------------------------------------- 1 | #' Generate Authorization Header 2 | #' 3 | #' Generate the basic authorization header for 4 | #' authentication. 5 | #' 6 | #' @param username,password Credentials to encode. 7 | #' @param string Base64 encoded string, as returned by 8 | #' [generateBasicAuth()]. 9 | #' 10 | #' @details Use the output of `generateBasicAuth` as 11 | #' input to `setAuthentication`. This makes it such 12 | #' that the `/metrics` endpoint requires the authentication 13 | #' defined. Then use the credentials in the prometheus 14 | #' configuration file. 15 | #' 16 | #' basic_auth: 17 | #' username: usr 18 | #' password: pwd 19 | #' 20 | #' @name auth 21 | #' 22 | #' @export 23 | generateBasicAuth <- function(username, password){ 24 | if(missing(username)) 25 | stop("Missing `username`", call. = FALSE) 26 | 27 | if(missing(password)) 28 | stop("Missing `password`", call. = FALSE) 29 | 30 | warning("Do not deploy or share this", call. = FALSE) 31 | 32 | checkInstalled("base64enc") 33 | string <- sprintf("%s:%s", username, password) 34 | encoded <- base64enc::base64encode(charToRaw(string)) 35 | paste("Basic", encoded) 36 | } 37 | 38 | #' @rdname auth 39 | #' @export 40 | setAuthentication <- function(string = Sys.getenv("TITAN_BASIC_AUTH")){ 41 | if(string == "") string <- NULL 42 | 43 | options(TITAN_BASIC_AUTH = string) 44 | } 45 | 46 | #' @rdname auth 47 | #' @export 48 | getAuthentication <- function(){ 49 | getOption("TITAN_BASIC_AUTH", NULL) 50 | } 51 | 52 | #' Retrieve Metrics from Endpoint 53 | #' 54 | #' Retrieve the metrics via the endpoint, 55 | #' useful for testing authentication. 56 | #' 57 | #' @param endpoint URL to the endpoint. 58 | #' @param auth Authentication string as returned 59 | #' by [generateBasicAuth()]. 60 | #' 61 | #' @export 62 | getMetrics <- function(endpoint, auth = NULL){ 63 | 64 | checkInstalled("httr") 65 | 66 | if(missing(endpoint)) 67 | stop("Must pass `endpoint`", call. = FALSE) 68 | 69 | endpoint <- parseEndpoint(endpoint) 70 | 71 | if(!is.null(auth)) 72 | httr::GET(endpoint, httr::add_headers(Authorization = auth)) 73 | else 74 | httr::GET(endpoint) 75 | } 76 | 77 | #' Parse endpoint 78 | #' 79 | #' @param endpoint URL to endpoint. 80 | #' 81 | #' @noRd 82 | #' @keywords internal 83 | parseEndpoint <- function(endpoint){ 84 | parsed <- httr::parse_url(endpoint) 85 | hasMetrics <- grepl("metrics", parsed$path) 86 | 87 | if(!hasMetrics){ 88 | path <- gsub("/$", "", parsed$path) 89 | path <- paste0(path, "/metrics") 90 | parsed$path <- path 91 | } 92 | 93 | httr::build_url(parsed) 94 | } -------------------------------------------------------------------------------- /man/Binned.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/binned.R 3 | \name{Binned} 4 | \alias{Binned} 5 | \title{Binned} 6 | \description{ 7 | Superclass for binned metrics: \link{Histogram} and \link{Summary}, 8 | used for convenience and DRY, do not use directly. 9 | } 10 | \section{Methods}{ 11 | \subsection{Public methods}{ 12 | \itemize{ 13 | \item \href{#method-new}{\code{Binned$new()}} 14 | \item \href{#method-observe}{\code{Binned$observe()}} 15 | \item \href{#method-clone}{\code{Binned$clone()}} 16 | } 17 | } 18 | \if{html}{\out{
}} 19 | \if{html}{\out{}} 20 | \if{latex}{\out{\hypertarget{method-new}{}}} 21 | \subsection{Method \code{new()}}{ 22 | \subsection{Usage}{ 23 | \if{html}{\out{
}}\preformatted{Binned$new( 24 | name, 25 | help, 26 | predicate, 27 | labels = NULL, 28 | unit = NULL, 29 | type = c("histogram", "summary") 30 | )}\if{html}{\out{
}} 31 | } 32 | 33 | \subsection{Arguments}{ 34 | \if{html}{\out{
}} 35 | \describe{ 36 | \item{\code{name}}{Name of the metric.} 37 | 38 | \item{\code{help}}{Help text describing the metric.} 39 | 40 | \item{\code{predicate}}{Predicate function to bin observations.} 41 | 42 | \item{\code{labels}}{Character vector of labels available.} 43 | 44 | \item{\code{unit}}{Unit of metric.} 45 | 46 | \item{\code{type}}{Metric type.} 47 | } 48 | \if{html}{\out{
}} 49 | } 50 | \subsection{Details}{ 51 | Initialise 52 | } 53 | 54 | } 55 | \if{html}{\out{
}} 56 | \if{html}{\out{}} 57 | \if{latex}{\out{\hypertarget{method-observe}{}}} 58 | \subsection{Method \code{observe()}}{ 59 | \subsection{Usage}{ 60 | \if{html}{\out{
}}\preformatted{Binned$observe(val, ...)}\if{html}{\out{
}} 61 | } 62 | 63 | \subsection{Arguments}{ 64 | \if{html}{\out{
}} 65 | \describe{ 66 | \item{\code{val}}{Value to observe and bin using the \code{predicate}.} 67 | 68 | \item{\code{...}}{Key value pair of labels.} 69 | } 70 | \if{html}{\out{
}} 71 | } 72 | \subsection{Details}{ 73 | Observe 74 | } 75 | 76 | } 77 | \if{html}{\out{
}} 78 | \if{html}{\out{}} 79 | \if{latex}{\out{\hypertarget{method-clone}{}}} 80 | \subsection{Method \code{clone()}}{ 81 | The objects of this class are cloneable with this method. 82 | \subsection{Usage}{ 83 | \if{html}{\out{
}}\preformatted{Binned$clone(deep = FALSE)}\if{html}{\out{
}} 84 | } 85 | 86 | \subsection{Arguments}{ 87 | \if{html}{\out{
}} 88 | \describe{ 89 | \item{\code{deep}}{Whether to make a deep clone.} 90 | } 91 | \if{html}{\out{
}} 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /man/Gauge.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/gauge.R 3 | \name{Gauge} 4 | \alias{Gauge} 5 | \title{Gauge} 6 | \description{ 7 | Gauge 8 | 9 | Gauge 10 | } 11 | \section{Super class}{ 12 | \code{\link[titan:MetricInterface]{titan::MetricInterface}} -> \code{Gauge} 13 | } 14 | \section{Methods}{ 15 | \subsection{Public methods}{ 16 | \itemize{ 17 | \item \href{#method-new}{\code{Gauge$new()}} 18 | \item \href{#method-clone}{\code{Gauge$clone()}} 19 | } 20 | } 21 | \if{html}{ 22 | \out{
Inherited methods} 23 | \itemize{ 24 | \item \out{}\href{../../titan/html/MetricInterface.html#method-dec}{\code{titan::MetricInterface$dec()}}\out{} 25 | \item \out{}\href{../../titan/html/MetricInterface.html#method-get}{\code{titan::MetricInterface$get()}}\out{} 26 | \item \out{}\href{../../titan/html/MetricInterface.html#method-inc}{\code{titan::MetricInterface$inc()}}\out{} 27 | \item \out{}\href{../../titan/html/MetricInterface.html#method-render}{\code{titan::MetricInterface$render()}}\out{} 28 | \item \out{}\href{../../titan/html/MetricInterface.html#method-set}{\code{titan::MetricInterface$set()}}\out{} 29 | } 30 | \out{
} 31 | } 32 | \if{html}{\out{
}} 33 | \if{html}{\out{}} 34 | \if{latex}{\out{\hypertarget{method-new}{}}} 35 | \subsection{Method \code{new()}}{ 36 | \subsection{Usage}{ 37 | \if{html}{\out{
}}\preformatted{Gauge$new(name, help, labels = NULL, unit = NULL)}\if{html}{\out{
}} 38 | } 39 | 40 | \subsection{Arguments}{ 41 | \if{html}{\out{
}} 42 | \describe{ 43 | \item{\code{name}}{Name of the metric.} 44 | 45 | \item{\code{help}}{Help text describing the metric.} 46 | 47 | \item{\code{labels}}{Character vector of labels available.} 48 | 49 | \item{\code{unit}}{Unit of metric.} 50 | } 51 | \if{html}{\out{
}} 52 | } 53 | \subsection{Details}{ 54 | Initialise 55 | } 56 | 57 | } 58 | \if{html}{\out{
}} 59 | \if{html}{\out{}} 60 | \if{latex}{\out{\hypertarget{method-clone}{}}} 61 | \subsection{Method \code{clone()}}{ 62 | The objects of this class are cloneable with this method. 63 | \subsection{Usage}{ 64 | \if{html}{\out{
}}\preformatted{Gauge$clone(deep = FALSE)}\if{html}{\out{
}} 65 | } 66 | 67 | \subsection{Arguments}{ 68 | \if{html}{\out{
}} 69 | \describe{ 70 | \item{\code{deep}}{Whether to make a deep clone.} 71 | } 72 | \if{html}{\out{
}} 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /docs/guide/authentication.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Authentication 3 | summary: Implement authentication to secure the metrics endpoint. 4 | authors: 5 | - John Coene 6 | --- 7 | 8 | # Authentication 9 | 10 | If you run shiny applications and plumber APIs on a secure server behind a proxy then you probably do not need authentication. If said applications are exposed publicly then you might want to use authentication so that the metrics are not publicly accessible. 11 | 12 | Prometheus supports [basic authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication), by default it does not use any, but optionally a user and password can be specified in the `prometheus.yml` so that these are used when hitting the `/metrics` endpoint. 13 | 14 | This means the endpoint needs to be secured with the same user and password. 15 | 16 | ## Securing the endpoint 17 | 18 | Use the helper function `generateBasicAuth` to create a token based on a username and password. Note that the function throws a warning: this function should not be left as-is in your application or plumber API. 19 | 20 | ```r 21 | generateBasicAuth( 22 | username = "titan", 23 | password = "secret2020!" 24 | ) 25 | ``` 26 | 27 | ``` 28 | [1] "Basic dGl0YW46c2VjcmV0MjAyMCE=" 29 | Warning message: 30 | Do not deploy or share this 31 | ``` 32 | 33 | !!! danger 34 | Do not use `generateBasicAuth` in your application or use the 35 | token in plain text: it exposes the password. 36 | 37 | 38 | Once the authentication token is created it can be used in `setAuthentication`. __The token should not be displayed in the code as in the example below,__ ideally should be an environment variable or option. 39 | 40 | ```r 41 | library(titan) 42 | library(shiny) 43 | 44 | setAuthentication("Basic dGl0YW46c2VjcmV0MjAyMCE=") 45 | 46 | ui <- fluidPage( 47 | h2("Secure endpoint") 48 | ) 49 | 50 | server <- function(input, output){} 51 | 52 | titanApp(ui, server, visits = "visits") 53 | ``` 54 | 55 | Running the application above then visiting `/metrics` should display `Unauthorized`. Indeed, visiting the endpoint from the browser executes a simple `GET` request that does not feature bear the token: it is unauthorised. 56 | 57 | One can open a new R session and use the convenience function `getMetrics` to test the endpoint. The function optionally takes a second argument, the authentication to use. 58 | 59 | ```r 60 | titan::getMetrics( 61 | "http://127.0.0.1:3145/", 62 | auth = "Basic dGl0YW46c2VjcmV0MjAyMCE=" 63 | ) 64 | ``` 65 | 66 | ``` 67 | Response [http://127.0.0.1:3145/metrics] 68 | Date: 2020-12-13 10:46 69 | Status: 200 70 | Content-Type: text/plain 71 | Size: 81 B 72 | # HELP visits Number of visits to the application 73 | # TYPE visits counter 74 | visits 1 75 | ``` 76 | 77 | This returns the metrics as it is, unlike via the browser, authenticated. 78 | 79 | ## Prometheus 80 | 81 | Then one can place the username and password in the job configuration. 82 | 83 | ```yml 84 | - job_name: my-api 85 | basic_auth: 86 | username: titan 87 | password: secret2020! 88 | ``` 89 | -------------------------------------------------------------------------------- /docs/about/monitoring.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: About Monitoring 3 | summary: A brief description of monitoring. 4 | authors: 5 | - John Coene 6 | --- 7 | 8 | # About Titan & Monitoring 9 | 10 | Titan is an R package to allow generating [Prometheus](https://prometheus.io/) metrics for R projects, including shiny, plumber, ambiorix, and more. 11 | 12 | When products rely on plumber APIs and teams rely on shiny applications these better be up and running. These services may crash, become overloaded, their response latency might increase, they may run out of resources, and more. In addition these scale non-linearly as the number of such services or containers grow; hence monitoring their health and being alerted when they raise too many errors or crash becomes crucial. 13 | 14 | Prometheus is an open-source monitoring and alerting solution. There are other such monitoring tools out there such as [Sensu](https://sensu.io/), [Nagios](https://www.nagios.org/), or [InfluxDB](https://www.influxdata.com/) which also focus on time series data. 15 | 16 | ## Push vs. Pull 17 | 18 | Prometheus differs from other services in many ways but the most important one probably is the fact that it reads the metrics from services from an endpoint provided by said service; it _pulls_ the data. 19 | 20 | Whereas many other solutions work the other way around; metrics are _pushed_ to the service for monitoring. The latter has the disadvantage that a tiny mistake can result in too much data being pushed to the monitoring service thereby essentially DDoSing yourself. 21 | 22 | Another difference is that Prometheus is built with containers in mind (Docker & Kubernetes). Though fully open-sourced today, it was initially developed at [Soundcloud](https://soundcloud.com/) which is a big advocate of and pioneer in using micro-services at scale. 23 | 24 | ## Black box vs. White box 25 | 26 | Up until recently most monitoring was probably "black box," applications were developed by some people and deployed by others. Devops had to set up monitoring of services with little knowledge of them. 27 | 28 | Nowadays, with the advent of micro-services where developers are responsible for the deployment of the apps they build, the industry is increasingly turning towards "white box" monitoring. 29 | 30 | !!! quote 31 | You build it you run it -- Jeff Bezos 32 | 33 | As the developer of the service you know more about it than anyone and can therefore setup more appropriate monitoring; Prometheus and titan let you do just that. 34 | 35 | This is particularly relevant to R programmers, most of which actually have to deploy the services they build. 36 | 37 | It's not as daunting as it sounds, it's rather natural, and you quickly come to enjoy it, a bit like [testthat](https://testthat.r-lib.org/) in a sense. 38 | 39 | ## Scale 40 | 41 | Monitoring is often mentioned along with large-scale micro-services where it is absolutely necessary. But this is not the only time when monitoring is useful. 42 | 43 | Surely, as the developer of an application, you want to keep an eye on its performances. Perhaps you were given performance objectives, like reducing the load time of a shiny application or reducing the response time of a plumber API endpoint. 44 | 45 | Even at a small scale monitoring is interesting and useful. Set up alerts when your services go down and fix them before your boss learns about it via a client. 46 | -------------------------------------------------------------------------------- /site/assets/javascripts/lunr/min/lunr.stemmer.support.min.js: -------------------------------------------------------------------------------- 1 | !function(r,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(r.lunr)}(this,function(){return function(r){r.stemmerSupport={Among:function(r,t,i,s){if(this.toCharArray=function(r){for(var t=r.length,i=new Array(t),s=0;s=i&&(e-=i,t[e>>3]&1<<(7&e)))return this.cursor++,!0}return!1},in_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e<=s&&e>=i&&(e-=i,t[e>>3]&1<<(7&e)))return this.cursor--,!0}return!1},out_grouping:function(t,i,s){if(this.cursors||e>3]&1<<(7&e)))return this.cursor++,!0}return!1},out_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e>s||e>3]&1<<(7&e)))return this.cursor--,!0}return!1},eq_s:function(t,i){if(this.limit-this.cursor>1),f=0,l=o0||e==s||c)break;c=!0}}for(;;){var _=t[s];if(o>=_.s_size){if(this.cursor=n+_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n+_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},find_among_b:function(t,i){for(var s=0,e=i,n=this.cursor,u=this.limit_backward,o=0,h=0,c=!1;;){for(var a=s+(e-s>>1),f=0,l=o=0;m--){if(n-l==u){f=-1;break}if(f=r.charCodeAt(n-1-l)-_.s[m])break;l++}if(f<0?(e=a,h=l):(s=a,o=l),e-s<=1){if(s>0||e==s||c)break;c=!0}}for(;;){var _=t[s];if(o>=_.s_size){if(this.cursor=n-_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n-_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},replace_s:function(t,i,s){var e=s.length-(i-t),n=r.substring(0,t),u=r.substring(i);return r=n+s+u,this.limit+=e,this.cursor>=i?this.cursor+=e:this.cursor>t&&(this.cursor=t),e},slice_check:function(){if(this.bra<0||this.bra>this.ket||this.ket>this.limit||this.limit>r.length)throw"faulty slice operation"},slice_from:function(r){this.slice_check(),this.replace_s(this.bra,this.ket,r)},slice_del:function(){this.slice_from("")},insert:function(r,t,i){var s=this.replace_s(r,t,i);r<=this.bra&&(this.bra+=s),r<=this.ket&&(this.ket+=s)},slice_to:function(){return this.slice_check(),r.substring(this.bra,this.ket)},eq_v_b:function(r){return this.eq_s_b(r.length,r)}}}},r.trimmerSupport={generateTrimmer:function(r){var t=new RegExp("^[^"+r+"]+"),i=new RegExp("[^"+r+"]+$");return function(r){return"function"==typeof r.update?r.update(function(r){return r.replace(t,"").replace(i,"")}):r.replace(t,"").replace(i,"")}}}}}); -------------------------------------------------------------------------------- /R/management.R: -------------------------------------------------------------------------------- 1 | #' Management 2 | #' 3 | #' Interact with the management API of Prometheus. 4 | #' 5 | #' @param uri URL to the prometheus service, including 6 | #' the port if necessary. It defaults to the `PROMETHEUS_URL` 7 | #' environment variable and if this is not set uses 8 | #' `http://localhost:9090`. 9 | #' @param prompt Whether to prompt the user for an input, 10 | #' used for the dangerous `PromQuit` which shuts down the 11 | #' Prometheus server. Defaults to `TRUE`. 12 | #' 13 | #' @section Functions: 14 | #' 15 | #' - `PromReload`: Tell Prometheus to reload the configuration file. 16 | #' Note that this only works if Prometheus was launched 17 | #' with the `--web.enable-lifecycle` flag. 18 | #' - `PromReady`: Check whether Prometeus is ready to serve traffic 19 | #' (i.e. respond to queries). Returns `TRUE` if Prometheus is ready. 20 | #' - `PromHealthy`: Checks Prometheus' health. 21 | #' Returns `TRUE` if Prometheus is healthy. 22 | #' - `PromQuit`: riggers a graceful shutdown of Prometheus. 23 | #' It's disabled by default and can be enabled via the 24 | #' `--web.enable-lifecycle` flag. 25 | #' 26 | #' @note All management-related functions start with a capital 27 | #' letter. 28 | #' 29 | #' @name mgmt 30 | #' 31 | #' @export 32 | PromReload <- function(uri = Sys.getenv("PROMETHEUS_URL", "http://localhost:9090")){ 33 | uri <- buildUrl(uri, "reload") 34 | response <- httr::POST(uri) 35 | status2bool(response) 36 | } 37 | 38 | #' @rdname mgmt 39 | #' @export 40 | PromHealthy <- function(uri = Sys.getenv("PROMETHEUS_URL", "http://localhost:9090")){ 41 | uri <- buildUrl(uri, "healthy") 42 | response <- httr::GET(uri) 43 | status2bool(response) 44 | } 45 | 46 | #' @rdname mgmt 47 | #' @export 48 | PromReady <- function(uri = Sys.getenv("PROMETHEUS_URL", "http://localhost:9090")){ 49 | uri <- buildUrl(uri, "ready") 50 | response <- httr::GET(uri) 51 | status2bool(response) 52 | } 53 | 54 | #' @rdname mgmt 55 | #' @export 56 | PromQuit <- function(uri = Sys.getenv("PROMETHEUS_URL", "http://localhost:9090"), prompt = TRUE){ 57 | uri <- buildUrl(uri, "quit") 58 | 59 | if(prompt){ 60 | answer <- readline("Are you sure you want to shut down Prometheus? (y/n) ") 61 | 62 | while(!tolower(answer) %in% c("y", "n")){ 63 | answer <- readline("Are you sure you want to shut down Prometheus? (y/n) ") 64 | } 65 | 66 | if(tolower(answer) == "n"){ 67 | cat("NOT shutting down Prometheus") 68 | return(invisible()) 69 | } 70 | } 71 | 72 | httr::POST(uri) 73 | } 74 | 75 | #' Clean the URL 76 | #' 77 | #' @param uri URL to clean. 78 | #' 79 | #' @noRd 80 | #' @keywords internal 81 | cleanUrl <- function(uri){ 82 | gsub("/$", "", uri) 83 | } 84 | 85 | #' Build the URL 86 | #' 87 | #' Builds the URL for the management API. 88 | #' 89 | #' @param uri Base URL. 90 | #' @param endpoint Endpoint to use. 91 | #' 92 | #' @noRd 93 | #' @keywords internal 94 | buildUrl <- function(uri, endpoint = c("healthy", "reload", "ready", "quit")){ 95 | checkInstalled("httr") 96 | 97 | endpoint <- match.arg(endpoint) 98 | 99 | uri <- cleanUrl(uri) 100 | sprintf("%s/-/%s", uri, endpoint) 101 | } 102 | 103 | #' Status as Boolean 104 | #' 105 | #' @param response Response from the httr package. 106 | #' 107 | #' @return `TRUE` if the status is `200` and `FALSE` otherwise. 108 | #' 109 | #' @noRd 110 | #' @keywords internal 111 | status2bool <- function(response){ 112 | status <- httr::status_code(response) 113 | 114 | if(status == 200L) 115 | return(TRUE) 116 | 117 | FALSE 118 | } -------------------------------------------------------------------------------- /man/Counter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/counter.R 3 | \name{Counter} 4 | \alias{Counter} 5 | \title{Counter} 6 | \description{ 7 | Create a new counter. 8 | } 9 | \section{Super class}{ 10 | \code{\link[titan:MetricInterface]{titan::MetricInterface}} -> \code{Counter} 11 | } 12 | \section{Methods}{ 13 | \subsection{Public methods}{ 14 | \itemize{ 15 | \item \href{#method-new}{\code{Counter$new()}} 16 | \item \href{#method-set}{\code{Counter$set()}} 17 | \item \href{#method-dec}{\code{Counter$dec()}} 18 | \item \href{#method-clone}{\code{Counter$clone()}} 19 | } 20 | } 21 | \if{html}{ 22 | \out{
Inherited methods} 23 | \itemize{ 24 | \item \out{}\href{../../titan/html/MetricInterface.html#method-get}{\code{titan::MetricInterface$get()}}\out{} 25 | \item \out{}\href{../../titan/html/MetricInterface.html#method-inc}{\code{titan::MetricInterface$inc()}}\out{} 26 | \item \out{}\href{../../titan/html/MetricInterface.html#method-render}{\code{titan::MetricInterface$render()}}\out{} 27 | } 28 | \out{
} 29 | } 30 | \if{html}{\out{
}} 31 | \if{html}{\out{}} 32 | \if{latex}{\out{\hypertarget{method-new}{}}} 33 | \subsection{Method \code{new()}}{ 34 | \subsection{Usage}{ 35 | \if{html}{\out{
}}\preformatted{Counter$new(name, help, labels = NULL, unit = NULL)}\if{html}{\out{
}} 36 | } 37 | 38 | \subsection{Arguments}{ 39 | \if{html}{\out{
}} 40 | \describe{ 41 | \item{\code{name}}{Name of the metric.} 42 | 43 | \item{\code{help}}{Help text describing the metric.} 44 | 45 | \item{\code{labels}}{Character vector of labels available.} 46 | 47 | \item{\code{unit}}{Unit of metric.} 48 | } 49 | \if{html}{\out{
}} 50 | } 51 | \subsection{Details}{ 52 | Initialise 53 | } 54 | 55 | } 56 | \if{html}{\out{
}} 57 | \if{html}{\out{}} 58 | \if{latex}{\out{\hypertarget{method-set}{}}} 59 | \subsection{Method \code{set()}}{ 60 | \subsection{Usage}{ 61 | \if{html}{\out{
}}\preformatted{Counter$set(val, ...)}\if{html}{\out{
}} 62 | } 63 | 64 | \subsection{Arguments}{ 65 | \if{html}{\out{
}} 66 | \describe{ 67 | \item{\code{val}}{Value to set the metric.} 68 | 69 | \item{\code{...}}{Key value pairs of labels.} 70 | } 71 | \if{html}{\out{
}} 72 | } 73 | \subsection{Details}{ 74 | Set the metrics to a specific value 75 | } 76 | 77 | } 78 | \if{html}{\out{
}} 79 | \if{html}{\out{}} 80 | \if{latex}{\out{\hypertarget{method-dec}{}}} 81 | \subsection{Method \code{dec()}}{ 82 | \subsection{Usage}{ 83 | \if{html}{\out{
}}\preformatted{Counter$dec()}\if{html}{\out{
}} 84 | } 85 | 86 | \subsection{Details}{ 87 | Cannot decrease a counter. 88 | } 89 | 90 | } 91 | \if{html}{\out{
}} 92 | \if{html}{\out{}} 93 | \if{latex}{\out{\hypertarget{method-clone}{}}} 94 | \subsection{Method \code{clone()}}{ 95 | The objects of this class are cloneable with this method. 96 | \subsection{Usage}{ 97 | \if{html}{\out{
}}\preformatted{Counter$clone(deep = FALSE)}\if{html}{\out{
}} 98 | } 99 | 100 | \subsection{Arguments}{ 101 | \if{html}{\out{
}} 102 | \describe{ 103 | \item{\code{deep}}{Whether to make a deep clone.} 104 | } 105 | \if{html}{\out{
}} 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | [![Travis build status](https://travis-ci.com/devOpifex/titan.svg?branch=master)](https://travis-ci.com/devOpifex/titan) 7 | [![AppVeyor build status](https://ci.appveyor.com/api/projects/status/github/devOpifex/titan?branch=master&svg=true)](https://ci.appveyor.com/project/devOpifex/titan) 8 | [![Coveralls test coverage](https://coveralls.io/repos/github/devOpifex/titan/badge.svg)](https://coveralls.io/r/devOpifex/titan?branch=master) 9 | 10 | 11 | [Docs](https://titan.opifex.org) | [Monitoring](https://titan.opifex.org/about/monitoring/) | [Install](https://titan.opifex.org/guide/installation/) | [Metrics](https://titan.opifex.org/guide/metrics/) 12 | 13 | [Prometheus](https://prometheus.io/) monitoring for shiny applications, plumber APIs, and other R web services. 14 | 15 |
16 | 17 | ## Acknowledgement 18 | 19 | I have put this package together in order to 1) grasp a better understanding of Prometheus metrics and 2) have some direct control over the source code of software I deploy for clients. I have written and re-written this three times before discovering [openmetrics](https://github.com/atheriel/openmetrics/), an R package that provides the same functionalities. I have taken much inspiration from it. 20 | 21 | ## Shiny 22 | 23 | A simple counter, a value that can only increase, and never decrease. 24 | 25 | ``` r 26 | library(titan) 27 | library(shiny) 28 | 29 | ui <- fluidPage( 30 | h1("Hello!") 31 | ) 32 | 33 | server <- function(input, output){} 34 | 35 | # use titanApp 36 | titanApp( 37 | ui, server, 38 | inputs = "inputs", 39 | visits = "visits", 40 | concurrent = "concurrent", 41 | duration = "duration" 42 | ) 43 | ``` 44 | 45 | 46 | ## Plumber 47 | 48 | Using titan in plumber. 49 | 50 | ```r 51 | #* Increment a counter 52 | #* @get / 53 | function() { 54 | return("Hello titan!") 55 | } 56 | 57 | #* Plot a histogram 58 | #* @serializer png 59 | #* @get /plot 60 | function() { 61 | rand <- rnorm(100) 62 | hist(rand) 63 | } 64 | ``` 65 | 66 | ```r 67 | library(plumber) 68 | 69 | titan::prTitan("file.R", latency = "latency") %>% 70 | pr_run() 71 | ``` 72 | 73 | ## Ambiorix 74 | 75 | Using titan with [ambiorix](https://ambiorix.john-coene.com). 76 | 77 | ```r 78 | library(titan) 79 | library(ambiorix) 80 | 81 | # basic counter 82 | c <- Counter$new( 83 | name = "visits_total", 84 | help = "Total visit to the site", 85 | labels = "path" 86 | ) 87 | 88 | app <- Ambiorix$new() 89 | 90 | app$get("/", function(req, res){ 91 | c$inc(path = "/") 92 | res$send("Using {titan} with {ambiorix}!") 93 | }) 94 | 95 | app$get("/about", function(req, res){ 96 | c$inc(path = "/about") 97 | res$send("About {titan} and {ambiorix}!") 98 | }) 99 | 100 | app$get("/metrics", function(req, res){ 101 | res$text(renderMetrics()) 102 | }) 103 | 104 | app$start() 105 | ``` 106 | 107 | ## Related work 108 | 109 | There are other packages out there that will let you serve Prometheus metrics. 110 | 111 | - [openmetrics](https://github.com/atheriel/openmetrics/) provides support for all metrics as well as authentication, and goes a step further in enforcing [OpenMetrics](https://openmetrics.io/) standards. 112 | - [pRometheus](https://github.com/cfmack/pRometheus/) Provides support for Gauge and Counter. 113 | 114 | ## Code of Conduct 115 | 116 | Please note that the titan project is released with a [Contributor Code of Conduct](https://contributor-covenant.org/version/2/0/CODE_OF_CONDUCT.html). By contributing to this project, you agree to abide by its terms. 117 | -------------------------------------------------------------------------------- /docs/deploy/prometheus.md: -------------------------------------------------------------------------------- 1 | # Prometheus 2 | 3 | Serving metrics with titan is only part of the story. Once those are served one must set up and deploy Prometheus so it can scrape those results. 4 | 5 | There are a few ways in which Prometheus can be deployed. The easiest I find is simply to install the binary and run is a service, this way it automatically restarts when the server reboots, etc. 6 | 7 | ## Installation 8 | 9 | Below we download the zipped [latest release](https://prometheus.io/download/) (at the time of writing this v2.23.0). 10 | 11 | ```bash 12 | wget https://github.com/prometheus/prometheus/releases/download/v2.23.0/prometheus-2.23.0.linux-amd64.tar.gz 13 | 14 | tar xvfz prometheus-2.23.0.linux-amd64.tar.gz 15 | mv prometheus-2.23.0.linux-amd64 prometheus 16 | ``` 17 | 18 | This downloads, unzips the files necessary to run Prometheus, then renames the directory from `prometheus-2.23.0.linux-amd64` to `prometheus`. Next you can move into that directory and run Prometheus manually to make sure all works well. 19 | 20 | ```bash 21 | cd ./prometheus 22 | ./prometheus 23 | ``` 24 | 25 | This should run prometheus and make it available on port `9090` by default and you should be able to see prometheus running at `:9090`. If you do not make sure that port `9090` is open on your server. 26 | 27 | The `prometheus.yml` file contains the "targets" to scrape, that is the plumber APIs and shiny applications to scrape the metrics. 28 | 29 | ## Service 30 | 31 | We can create a new service to easily have Prometheus run in the background, restart when needed, etc. 32 | 33 | ``` 34 | vi /etc/systemd/system/prometheus.service 35 | ``` 36 | 37 | In that service place the following. 38 | 39 | ``` 40 | [Unit] 41 | Description=Prometheus 42 | Documentation=https://prometheus.io/docs/introduction/overview/ 43 | Wants=network-online.target 44 | After=network-online.target 45 | 46 | [Service] 47 | Type=simple 48 | User=root 49 | ExecReload=/bin/kill -HUP \$MAINPID 50 | ExecStart=/prometheus/prometheus \ 51 | --config.file=/prometheus/prometheus.yml \ 52 | --web.enable-lifecycle 53 | 54 | SyslogIdentifier=prometheus 55 | Restart=always 56 | 57 | [Install] 58 | WantedBy=multi-user.target 59 | ``` 60 | 61 | This essentially will create a new service, very similar to shiny-server. Note the use of `--web.enable-lifecycle` to reload the configuration file by executing a `POST` query. 62 | 63 | This creates the service it can then be run after reloading the daemon. 64 | 65 | ``` 66 | sudo systemctl daemon-reload 67 | sudo systemctl start prometheus 68 | sudo systemctl enable prometheus 69 | ``` 70 | 71 | Running `sudo systemctl status prometheus` will show whether the service is running correctly. 72 | 73 | ## Configuration file 74 | 75 | We have yet to explore the configuration file. Below is an example of a job to scrape a shiny application. 76 | 77 | ```yml 78 | scrape_configs: 79 | - job_name: my-application 80 | scheme: 'http' 81 | targets: ['shiny-server.com'] 82 | metrics_path: 'myapp/metrics' 83 | basic_auth: 84 | username: titan 85 | password: secret2020! 86 | - job_name: my-other-application 87 | scheme: 'http' 88 | targets: ['shiny-server.com'] 89 | metrics_path: 'anotherApp/metrics' 90 | basic_auth: 91 | username: myName 92 | password: securePassword 93 | ``` 94 | 95 | ## Reload Configuration 96 | 97 | Once changes have been made to the configuration we must tell prometheus to reload it. Since we set the flag `--web.enable-lifecycle` when launching the Prometheus service we can simply make a `POST` request to the `/-/reload` endpoint of Prometheus to reload the configuration file. 98 | 99 | ``` 100 | curl -X POST http://localhost:9090/-/reload 101 | ``` 102 | 103 | There is a convenience function in titan to do so from R. 104 | 105 | ```r 106 | PromReload("http://localhost:9090") 107 | ``` 108 | 109 | !!! tip 110 | All functions that pertain to the Management of the Prometheus 111 | server start with a capital letter. 112 | 113 | From there onwards it's just a matter of adding jobs to the configuration file and reload it. 114 | 115 | Happy tracking! 116 | -------------------------------------------------------------------------------- /site/assets/javascripts/lunr/min/lunr.sv.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Lunr languages, `Swedish` language 3 | * https://github.com/MihaiValentin/lunr-languages 4 | * 5 | * Copyright 2014, Mihai Valentin 6 | * http://www.mozilla.org/MPL/ 7 | */ 8 | /*! 9 | * based on 10 | * Snowball JavaScript Library v0.3 11 | * http://code.google.com/p/urim/ 12 | * http://snowball.tartarus.org/ 13 | * 14 | * Copyright 2010, Oleg Mazko 15 | * http://www.mozilla.org/MPL/ 16 | */ 17 | 18 | !function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.sv=function(){this.pipeline.reset(),this.pipeline.add(e.sv.trimmer,e.sv.stopWordFilter,e.sv.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.sv.stemmer))},e.sv.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.sv.trimmer=e.trimmerSupport.generateTrimmer(e.sv.wordCharacters),e.Pipeline.registerFunction(e.sv.trimmer,"trimmer-sv"),e.sv.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,t=new function(){function e(){var e,r=w.cursor+3;if(o=w.limit,0<=r||r<=w.limit){for(a=r;;){if(e=w.cursor,w.in_grouping(l,97,246)){w.cursor=e;break}if(w.cursor=e,w.cursor>=w.limit)return;w.cursor++}for(;!w.out_grouping(l,97,246);){if(w.cursor>=w.limit)return;w.cursor++}o=w.cursor,o=o&&(w.limit_backward=o,w.cursor=w.limit,w.ket=w.cursor,e=w.find_among_b(u,37),w.limit_backward=r,e))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:w.in_grouping_b(d,98,121)&&w.slice_del()}}function i(){var e=w.limit_backward;w.cursor>=o&&(w.limit_backward=o,w.cursor=w.limit,w.find_among_b(c,7)&&(w.cursor=w.limit,w.ket=w.cursor,w.cursor>w.limit_backward&&(w.bra=--w.cursor,w.slice_del())),w.limit_backward=e)}function s(){var e,r;if(w.cursor>=o){if(r=w.limit_backward,w.limit_backward=o,w.cursor=w.limit,w.ket=w.cursor,e=w.find_among_b(m,5))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:w.slice_from("lös");break;case 3:w.slice_from("full")}w.limit_backward=r}}var a,o,u=[new r("a",-1,1),new r("arna",0,1),new r("erna",0,1),new r("heterna",2,1),new r("orna",0,1),new r("ad",-1,1),new r("e",-1,1),new r("ade",6,1),new r("ande",6,1),new r("arne",6,1),new r("are",6,1),new r("aste",6,1),new r("en",-1,1),new r("anden",12,1),new r("aren",12,1),new r("heten",12,1),new r("ern",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",18,1),new r("or",-1,1),new r("s",-1,2),new r("as",21,1),new r("arnas",22,1),new r("ernas",22,1),new r("ornas",22,1),new r("es",21,1),new r("ades",26,1),new r("andes",26,1),new r("ens",21,1),new r("arens",29,1),new r("hetens",29,1),new r("erns",21,1),new r("at",-1,1),new r("andet",-1,1),new r("het",-1,1),new r("ast",-1,1)],c=[new r("dd",-1,-1),new r("gd",-1,-1),new r("nn",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1),new r("tt",-1,-1)],m=[new r("ig",-1,1),new r("lig",0,1),new r("els",-1,1),new r("fullt",-1,3),new r("löst",-1,2)],l=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,24,0,32],d=[119,127,149],w=new n;this.setCurrent=function(e){w.setCurrent(e)},this.getCurrent=function(){return w.getCurrent()},this.stem=function(){var r=w.cursor;return e(),w.limit_backward=r,w.cursor=w.limit,t(),w.cursor=w.limit,i(),w.cursor=w.limit,s(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return t.setCurrent(e),t.stem(),t.getCurrent()}):(t.setCurrent(e),t.stem(),t.getCurrent())}}(),e.Pipeline.registerFunction(e.sv.stemmer,"stemmer-sv"),e.sv.stopWordFilter=e.generateStopWordFilter("alla allt att av blev bli blir blivit de dem den denna deras dess dessa det detta dig din dina ditt du där då efter ej eller en er era ert ett från för ha hade han hans har henne hennes hon honom hur här i icke ingen inom inte jag ju kan kunde man med mellan men mig min mina mitt mot mycket ni nu när någon något några och om oss på samma sedan sig sin sina sitta själv skulle som så sådan sådana sådant till under upp ut utan vad var vara varför varit varje vars vart vem vi vid vilka vilkas vilken vilket vår våra vårt än är åt över".split(" ")),e.Pipeline.registerFunction(e.sv.stopWordFilter,"stopWordFilter-sv")}}); -------------------------------------------------------------------------------- /site/assets/javascripts/lunr/min/lunr.da.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Lunr languages, `Danish` language 3 | * https://github.com/MihaiValentin/lunr-languages 4 | * 5 | * Copyright 2014, Mihai Valentin 6 | * http://www.mozilla.org/MPL/ 7 | */ 8 | /*! 9 | * based on 10 | * Snowball JavaScript Library v0.3 11 | * http://code.google.com/p/urim/ 12 | * http://snowball.tartarus.org/ 13 | * 14 | * Copyright 2010, Oleg Mazko 15 | * http://www.mozilla.org/MPL/ 16 | */ 17 | 18 | !function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.da=function(){this.pipeline.reset(),this.pipeline.add(e.da.trimmer,e.da.stopWordFilter,e.da.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.da.stemmer))},e.da.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.da.trimmer=e.trimmerSupport.generateTrimmer(e.da.wordCharacters),e.Pipeline.registerFunction(e.da.trimmer,"trimmer-da"),e.da.stemmer=function(){var r=e.stemmerSupport.Among,i=e.stemmerSupport.SnowballProgram,n=new function(){function e(){var e,r=f.cursor+3;if(d=f.limit,0<=r&&r<=f.limit){for(a=r;;){if(e=f.cursor,f.in_grouping(w,97,248)){f.cursor=e;break}if(f.cursor=e,e>=f.limit)return;f.cursor++}for(;!f.out_grouping(w,97,248);){if(f.cursor>=f.limit)return;f.cursor++}d=f.cursor,d=d&&(r=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,e=f.find_among_b(c,32),f.limit_backward=r,e))switch(f.bra=f.cursor,e){case 1:f.slice_del();break;case 2:f.in_grouping_b(p,97,229)&&f.slice_del()}}function t(){var e,r=f.limit-f.cursor;f.cursor>=d&&(e=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,f.find_among_b(l,4)?(f.bra=f.cursor,f.limit_backward=e,f.cursor=f.limit-r,f.cursor>f.limit_backward&&(f.cursor--,f.bra=f.cursor,f.slice_del())):f.limit_backward=e)}function s(){var e,r,i,n=f.limit-f.cursor;if(f.ket=f.cursor,f.eq_s_b(2,"st")&&(f.bra=f.cursor,f.eq_s_b(2,"ig")&&f.slice_del()),f.cursor=f.limit-n,f.cursor>=d&&(r=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,e=f.find_among_b(m,5),f.limit_backward=r,e))switch(f.bra=f.cursor,e){case 1:f.slice_del(),i=f.limit-f.cursor,t(),f.cursor=f.limit-i;break;case 2:f.slice_from("løs")}}function o(){var e;f.cursor>=d&&(e=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,f.out_grouping_b(w,97,248)?(f.bra=f.cursor,u=f.slice_to(u),f.limit_backward=e,f.eq_v_b(u)&&f.slice_del()):f.limit_backward=e)}var a,d,u,c=[new r("hed",-1,1),new r("ethed",0,1),new r("ered",-1,1),new r("e",-1,1),new r("erede",3,1),new r("ende",3,1),new r("erende",5,1),new r("ene",3,1),new r("erne",3,1),new r("ere",3,1),new r("en",-1,1),new r("heden",10,1),new r("eren",10,1),new r("er",-1,1),new r("heder",13,1),new r("erer",13,1),new r("s",-1,2),new r("heds",16,1),new r("es",16,1),new r("endes",18,1),new r("erendes",19,1),new r("enes",18,1),new r("ernes",18,1),new r("eres",18,1),new r("ens",16,1),new r("hedens",24,1),new r("erens",24,1),new r("ers",16,1),new r("ets",16,1),new r("erets",28,1),new r("et",-1,1),new r("eret",30,1)],l=[new r("gd",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1)],m=[new r("ig",-1,1),new r("lig",0,1),new r("elig",1,1),new r("els",-1,1),new r("løst",-1,2)],w=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],p=[239,254,42,3,0,0,0,0,0,0,0,0,0,0,0,0,16],f=new i;this.setCurrent=function(e){f.setCurrent(e)},this.getCurrent=function(){return f.getCurrent()},this.stem=function(){var r=f.cursor;return e(),f.limit_backward=r,f.cursor=f.limit,n(),f.cursor=f.limit,t(),f.cursor=f.limit,s(),f.cursor=f.limit,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.da.stemmer,"stemmer-da"),e.da.stopWordFilter=e.generateStopWordFilter("ad af alle alt anden at blev blive bliver da de dem den denne der deres det dette dig din disse dog du efter eller en end er et for fra ham han hans har havde have hende hendes her hos hun hvad hvis hvor i ikke ind jeg jer jo kunne man mange med meget men mig min mine mit mod ned noget nogle nu når og også om op os over på selv sig sin sine sit skal skulle som sådan thi til ud under var vi vil ville vor være været".split(" ")),e.Pipeline.registerFunction(e.da.stopWordFilter,"stopWordFilter-da")}}); -------------------------------------------------------------------------------- /site/assets/javascripts/lunr/min/lunr.no.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Lunr languages, `Norwegian` language 3 | * https://github.com/MihaiValentin/lunr-languages 4 | * 5 | * Copyright 2014, Mihai Valentin 6 | * http://www.mozilla.org/MPL/ 7 | */ 8 | /*! 9 | * based on 10 | * Snowball JavaScript Library v0.3 11 | * http://code.google.com/p/urim/ 12 | * http://snowball.tartarus.org/ 13 | * 14 | * Copyright 2010, Oleg Mazko 15 | * http://www.mozilla.org/MPL/ 16 | */ 17 | 18 | !function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.no=function(){this.pipeline.reset(),this.pipeline.add(e.no.trimmer,e.no.stopWordFilter,e.no.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.no.stemmer))},e.no.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.no.trimmer=e.trimmerSupport.generateTrimmer(e.no.wordCharacters),e.Pipeline.registerFunction(e.no.trimmer,"trimmer-no"),e.no.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){function e(){var e,r=w.cursor+3;if(a=w.limit,0<=r||r<=w.limit){for(s=r;;){if(e=w.cursor,w.in_grouping(d,97,248)){w.cursor=e;break}if(e>=w.limit)return;w.cursor=e+1}for(;!w.out_grouping(d,97,248);){if(w.cursor>=w.limit)return;w.cursor++}a=w.cursor,a=a&&(r=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,e=w.find_among_b(m,29),w.limit_backward=r,e))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:n=w.limit-w.cursor,w.in_grouping_b(c,98,122)?w.slice_del():(w.cursor=w.limit-n,w.eq_s_b(1,"k")&&w.out_grouping_b(d,97,248)&&w.slice_del());break;case 3:w.slice_from("er")}}function t(){var e,r=w.limit-w.cursor;w.cursor>=a&&(e=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,w.find_among_b(u,2)?(w.bra=w.cursor,w.limit_backward=e,w.cursor=w.limit-r,w.cursor>w.limit_backward&&(w.cursor--,w.bra=w.cursor,w.slice_del())):w.limit_backward=e)}function o(){var e,r;w.cursor>=a&&(r=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,e=w.find_among_b(l,11),e?(w.bra=w.cursor,w.limit_backward=r,1==e&&w.slice_del()):w.limit_backward=r)}var s,a,m=[new r("a",-1,1),new r("e",-1,1),new r("ede",1,1),new r("ande",1,1),new r("ende",1,1),new r("ane",1,1),new r("ene",1,1),new r("hetene",6,1),new r("erte",1,3),new r("en",-1,1),new r("heten",9,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",12,1),new r("s",-1,2),new r("as",14,1),new r("es",14,1),new r("edes",16,1),new r("endes",16,1),new r("enes",16,1),new r("hetenes",19,1),new r("ens",14,1),new r("hetens",21,1),new r("ers",14,1),new r("ets",14,1),new r("et",-1,1),new r("het",25,1),new r("ert",-1,3),new r("ast",-1,1)],u=[new r("dt",-1,-1),new r("vt",-1,-1)],l=[new r("leg",-1,1),new r("eleg",0,1),new r("ig",-1,1),new r("eig",2,1),new r("lig",2,1),new r("elig",4,1),new r("els",-1,1),new r("lov",-1,1),new r("elov",7,1),new r("slov",7,1),new r("hetslov",9,1)],d=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],c=[119,125,149,1],w=new n;this.setCurrent=function(e){w.setCurrent(e)},this.getCurrent=function(){return w.getCurrent()},this.stem=function(){var r=w.cursor;return e(),w.limit_backward=r,w.cursor=w.limit,i(),w.cursor=w.limit,t(),w.cursor=w.limit,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.no.stemmer,"stemmer-no"),e.no.stopWordFilter=e.generateStopWordFilter("alle at av bare begge ble blei bli blir blitt både båe da de deg dei deim deira deires dem den denne der dere deres det dette di din disse ditt du dykk dykkar då eg ein eit eitt eller elles en enn er et ett etter for fordi fra før ha hadde han hans har hennar henne hennes her hjå ho hoe honom hoss hossen hun hva hvem hver hvilke hvilken hvis hvor hvordan hvorfor i ikke ikkje ikkje ingen ingi inkje inn inni ja jeg kan kom korleis korso kun kunne kva kvar kvarhelst kven kvi kvifor man mange me med medan meg meget mellom men mi min mine mitt mot mykje ned no noe noen noka noko nokon nokor nokre nå når og også om opp oss over på samme seg selv si si sia sidan siden sin sine sitt sjøl skal skulle slik so som som somme somt så sånn til um upp ut uten var vart varte ved vere verte vi vil ville vore vors vort vår være være vært å".split(" ")),e.Pipeline.registerFunction(e.no.stopWordFilter,"stopWordFilter-no")}}); -------------------------------------------------------------------------------- /R/binned.R: -------------------------------------------------------------------------------- 1 | #' Binned 2 | #' 3 | #' Superclass for binned metrics: [Histogram] and [Summary], 4 | #' used for convenience and DRY, do not use directly. 5 | #' 6 | #' @export 7 | Binned <- R6::R6Class( 8 | "Binned", 9 | public = list( 10 | #' @details Initialise 11 | #' 12 | #' @param name Name of the metric. 13 | #' @param help Help text describing the metric. 14 | #' @param labels Character vector of labels available. 15 | #' @param unit Unit of metric. 16 | #' @param type Metric type. 17 | #' @param predicate Predicate function to bin observations. 18 | initialize = function(name, help, predicate, 19 | labels = NULL, unit = NULL, 20 | type = c("histogram", "summary")){ 21 | 22 | type <- match.arg(type) 23 | private$.type <- type 24 | 25 | # names 26 | nameCount <- sprintf("%s_count", name) 27 | nameSum <- sprintf("%s_sum", name) 28 | 29 | bucketLabel <- ifelse(type == "histogram", "le", "quantile") 30 | labelsBucket <- c(labels, bucketLabel) 31 | 32 | # predicate 33 | if(missing(predicate)) 34 | stop("Missing `predicate`", call. = FALSE) 35 | 36 | if(!is.function(predicate)) 37 | stop("Predicate must be a function") 38 | 39 | private$.predicate <- predicate 40 | 41 | private$.buckets <- MetricInterface$new( 42 | name, help, labels = labelsBucket, unit = unit, 43 | type = type 44 | ) 45 | 46 | private$.count <- MetricInterface$new( 47 | nameCount, help, labels = labels, unit = unit, 48 | type = "counter", renderMeta = FALSE 49 | ) 50 | 51 | private$.sum <- MetricInterface$new( 52 | nameSum, help, labels = labels, unit = unit, 53 | type = "counter", renderMeta = FALSE 54 | ) 55 | }, 56 | #' @details Observe 57 | #' @param val Value to observe and bin using the `predicate`. 58 | #' @param ... Key value pair of labels. 59 | observe = function(val, ...){ 60 | pred <- private$.predicate(val) 61 | 62 | if(!is.bucket(pred)){ 63 | warning( 64 | "[IGNORING] `predicate` must return an object ", 65 | "of class `bucket`", 66 | call. = FALSE 67 | ) 68 | return(invisible()) 69 | } 70 | 71 | if(private$.type == "histogram") 72 | args <- list(1, le = pred$label, ...) 73 | else 74 | args <- list(pred$value, quantile = pred$label, ...) 75 | 76 | do.call(private$.buckets$inc, args) 77 | 78 | private$.count$inc(1, ...) 79 | private$.sum$inc(pred$value, ...) 80 | } 81 | ), 82 | private = list( 83 | .buckets = NULL, 84 | .count = NULL, 85 | .sum = NULL, 86 | .predicate = NULL, 87 | .type = "histogram" 88 | ) 89 | ) 90 | 91 | #' Histogram 92 | #' 93 | #' Create a histogram to bin observed metrics. Values 94 | #' observed at preprocessed with the `predicate` function 95 | #' passed at instantiation. This `predicate` function, 96 | #' **must** return an object of class `bucket`, see 97 | #' [bucket()]. 98 | #' 99 | #' While the [Histogram] counts up the values in each 100 | #' bucket, the [Summary] sums these up over time. 101 | #' 102 | #' @export 103 | Histogram <- R6::R6Class( 104 | "Histogram", 105 | inherit = Binned, 106 | public = list( 107 | #' @details Initialise 108 | #' 109 | #' @param name Name of the metric. 110 | #' @param help Help text describing the metric. 111 | #' @param labels Character vector of labels available. 112 | #' @param unit Unit of metric. 113 | #' @param predicate Predicate function to bin observations. 114 | initialize = function(name, help, predicate, 115 | labels = NULL, unit = NULL){ 116 | 117 | super$initialize( 118 | name, help, predicate, labels, unit, "histogram" 119 | ) 120 | 121 | } 122 | ) 123 | ) 124 | 125 | #' Summary 126 | #' 127 | #' Create a histogram to bin observed metrics. Values 128 | #' observed at preprocessed with the `predicate` function 129 | #' passed at instantiation. This `predicate` function, 130 | #' **must** return an object of class `bucket`, see 131 | #' [bucket()]. 132 | #' 133 | #' While the [Histogram] counts up the values in each 134 | #' bucket, the [Summary] sums these up over time. 135 | #' 136 | #' @export 137 | Summary <- R6::R6Class( 138 | "Summary", 139 | inherit = Binned, 140 | public = list( 141 | #' @details Initialise 142 | #' 143 | #' @param name Name of the metric. 144 | #' @param help Help text describing the metric. 145 | #' @param labels Character vector of labels available. 146 | #' @param unit Unit of metric. 147 | #' @param predicate Predicate function to bin observations. 148 | initialize = function(name, help, predicate, 149 | labels = NULL, unit = NULL){ 150 | 151 | super$initialize( 152 | name, help, predicate, labels, unit, "summary" 153 | ) 154 | 155 | } 156 | ) 157 | ) 158 | -------------------------------------------------------------------------------- /R/shiny.R: -------------------------------------------------------------------------------- 1 | #' @importFrom utils getFromNamespace 2 | res <- getFromNamespace("httpResponse", "shiny") 3 | 4 | #' Serve Application with metrics 5 | #' 6 | #' Use this function like you would use [shiny::shinyApp()]. 7 | #' 8 | #' @param ui,server UI definition and server function 9 | #' as passed to [shiny::shinyApp()]. 10 | #' @param ... Any other arguments passed to [shiny::shinyApp()]. 11 | #' @param inputs Pass a character string to use as metric name 12 | #' to track input changes. Leave on `NULL` to not track. 13 | #' @param visits Pass a character string to use as metric name 14 | #' to track the number of visits. Leave on `NULL` to not track. 15 | #' @param concurrent Pass a character string to use as metric name 16 | #' to track the number of concurrent users. 17 | #' Leave on `NULL` to not track. 18 | #' @param duration Pass a character string to use as metric name 19 | #' to track session duration. Leave on `NULL` to not track. 20 | #' 21 | #' @export 22 | titanApp <- function(ui, server, ..., inputs = NULL, visits = NULL, 23 | concurrent = NULL, duration = NULL){ 24 | # create native app 25 | app <- shiny::shinyApp(ui, server, ...) 26 | 27 | # defaults 28 | inputCounter <- NULL 29 | visitsCounter <- NULL 30 | concurrentGauge <- NULL 31 | durationHist <- NULL 32 | 33 | if(is.logical(inputs)) 34 | stop("`inputs` must be a character string", call. = FALSE) 35 | 36 | if(is.logical(visits)) 37 | stop("`visits` must be a character string", call. = FALSE) 38 | 39 | if(is.logical(concurrent)) 40 | stop("`concurrent` must be a character string", call. = FALSE) 41 | 42 | if(is.logical(duration)) 43 | stop("`duration` must be a character string", call. = FALSE) 44 | 45 | if(!is.null(inputs)) 46 | inputCounter <- Counter$new( 47 | inputs, 48 | "Number of times inputs were triggered", 49 | labels = "name" 50 | ) 51 | 52 | if(!is.null(visits)) 53 | visitsCounter <- Counter$new( 54 | visits, 55 | "Number of visits to the application" 56 | ) 57 | 58 | if(!is.null(concurrent)) 59 | concurrentGauge <- Gauge$new( 60 | concurrent, 61 | "Number of concurrent users" 62 | ) 63 | 64 | if(!is.null(duration)) 65 | durationHist <- Histogram$new( 66 | duration, 67 | "Session duration", 68 | predicate = sessionDuration 69 | ) 70 | 71 | 72 | # hijack server function 73 | serverFnSource <- app$serverFuncSource() 74 | 75 | app$serverFuncSource <- function(){ 76 | function(input, output, session){ 77 | 78 | # duration 79 | if(!is.null(durationHist)) 80 | start <- Sys.time() 81 | 82 | # increment 83 | if(!is.null(concurrentGauge)) 84 | concurrentGauge$inc() 85 | 86 | # track inputs 87 | if(!is.null(inputCounter)){ 88 | session$onInputReceived(function(val){ 89 | inputNames <- names(val) 90 | 91 | sapply(inputNames, function(n){ 92 | inputCounter$inc(1, name = n) 93 | }) 94 | 95 | return(val) 96 | }) 97 | } 98 | 99 | # track visits 100 | if(!is.null(visitsCounter)) 101 | visitsCounter$inc() 102 | 103 | # onsessionend 104 | onEnd <- function(concurrentGauge, durationHist){ 105 | return( 106 | function(){ 107 | if(!is.null(concurrentGauge)) 108 | concurrentGauge$dec() 109 | 110 | if(!is.null(durationHist)){ 111 | end <- Sys.time() 112 | diff <- end - start 113 | durationHist$observe(diff) 114 | } 115 | } 116 | ) 117 | } 118 | 119 | onSessionEnd <- onEnd(concurrentGauge, durationHist) 120 | shiny::onSessionEnded(onSessionEnd) 121 | 122 | # fails if user only uses input and output 123 | # if fails try without session 124 | app <- tryCatch( 125 | serverFnSource(input, output, session), 126 | error = function(e) e 127 | ) 128 | 129 | if(inherits(app, "error")) 130 | app <- serverFnSource(input, output) 131 | 132 | app 133 | } 134 | } 135 | 136 | # get handler 137 | handlerManager <- getFromNamespace("handlerManager", "shiny") 138 | 139 | # add handler 140 | handlerManager$addHandler(shinyHandler, "/metrics") 141 | 142 | return(app) 143 | } 144 | 145 | 146 | sessionDuration <- function(val){ 147 | 148 | v <- as.integer(val) 149 | 150 | if(v < 30) 151 | return(bucket("30", v)) 152 | 153 | if(v < 45) 154 | return(bucket("45", v)) 155 | 156 | if(v < 60) 157 | return(bucket("60", v)) 158 | 159 | if(v < 120) 160 | return(bucket("120", v)) 161 | 162 | if(v < 300) 163 | return(bucket("300", v)) 164 | 165 | if(v < 600) 166 | return(bucket("600", v)) 167 | 168 | if(v < 1200) 169 | return(bucket("1200", v)) 170 | 171 | if(v < 1800) 172 | return(bucket("1800", v)) 173 | 174 | bucket("+inf", v) 175 | } -------------------------------------------------------------------------------- /man/MetricInterface.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/metrics.R 3 | \name{MetricInterface} 4 | \alias{MetricInterface} 5 | \title{Metrics Interface} 6 | \description{ 7 | Exposed interface to the metrics. 8 | } 9 | \section{Methods}{ 10 | \subsection{Public methods}{ 11 | \itemize{ 12 | \item \href{#method-new}{\code{MetricInterface$new()}} 13 | \item \href{#method-set}{\code{MetricInterface$set()}} 14 | \item \href{#method-get}{\code{MetricInterface$get()}} 15 | \item \href{#method-inc}{\code{MetricInterface$inc()}} 16 | \item \href{#method-dec}{\code{MetricInterface$dec()}} 17 | \item \href{#method-render}{\code{MetricInterface$render()}} 18 | \item \href{#method-clone}{\code{MetricInterface$clone()}} 19 | } 20 | } 21 | \if{html}{\out{
}} 22 | \if{html}{\out{}} 23 | \if{latex}{\out{\hypertarget{method-new}{}}} 24 | \subsection{Method \code{new()}}{ 25 | \subsection{Usage}{ 26 | \if{html}{\out{
}}\preformatted{MetricInterface$new( 27 | name, 28 | help, 29 | labels = NULL, 30 | unit = NULL, 31 | type = c("gauge", "counter", "histogram", "summary"), 32 | renderMeta = TRUE 33 | )}\if{html}{\out{
}} 34 | } 35 | 36 | \subsection{Arguments}{ 37 | \if{html}{\out{
}} 38 | \describe{ 39 | \item{\code{name}}{Name of the metric.} 40 | 41 | \item{\code{help}}{Help text describing the metric.} 42 | 43 | \item{\code{labels}}{Character vector of labels available.} 44 | 45 | \item{\code{unit}}{Unit of metric.} 46 | 47 | \item{\code{type}}{Metric type.} 48 | 49 | \item{\code{renderMeta}}{Whether to render the metadata.} 50 | } 51 | \if{html}{\out{
}} 52 | } 53 | \subsection{Details}{ 54 | Initialise 55 | } 56 | 57 | } 58 | \if{html}{\out{
}} 59 | \if{html}{\out{}} 60 | \if{latex}{\out{\hypertarget{method-set}{}}} 61 | \subsection{Method \code{set()}}{ 62 | \subsection{Usage}{ 63 | \if{html}{\out{
}}\preformatted{MetricInterface$set(val, ...)}\if{html}{\out{
}} 64 | } 65 | 66 | \subsection{Arguments}{ 67 | \if{html}{\out{
}} 68 | \describe{ 69 | \item{\code{val}}{Value to set the metric.} 70 | 71 | \item{\code{...}}{Key value pairs of labels.} 72 | } 73 | \if{html}{\out{
}} 74 | } 75 | \subsection{Details}{ 76 | Set the metric to a current value given labels. 77 | } 78 | 79 | } 80 | \if{html}{\out{
}} 81 | \if{html}{\out{}} 82 | \if{latex}{\out{\hypertarget{method-get}{}}} 83 | \subsection{Method \code{get()}}{ 84 | \subsection{Usage}{ 85 | \if{html}{\out{
}}\preformatted{MetricInterface$get(...)}\if{html}{\out{
}} 86 | } 87 | 88 | \subsection{Arguments}{ 89 | \if{html}{\out{
}} 90 | \describe{ 91 | \item{\code{...}}{Key value pairs of labels.} 92 | } 93 | \if{html}{\out{
}} 94 | } 95 | \subsection{Details}{ 96 | Retrieve the value of a metric given labels 97 | } 98 | 99 | } 100 | \if{html}{\out{
}} 101 | \if{html}{\out{}} 102 | \if{latex}{\out{\hypertarget{method-inc}{}}} 103 | \subsection{Method \code{inc()}}{ 104 | \subsection{Usage}{ 105 | \if{html}{\out{
}}\preformatted{MetricInterface$inc(val = 1, ...)}\if{html}{\out{
}} 106 | } 107 | 108 | \subsection{Arguments}{ 109 | \if{html}{\out{
}} 110 | \describe{ 111 | \item{\code{val}}{Value to increase the metric.} 112 | 113 | \item{\code{...}}{Key value pairs of labels.} 114 | } 115 | \if{html}{\out{
}} 116 | } 117 | \subsection{Details}{ 118 | Increase the metric to a current value given labels. 119 | } 120 | 121 | } 122 | \if{html}{\out{
}} 123 | \if{html}{\out{}} 124 | \if{latex}{\out{\hypertarget{method-dec}{}}} 125 | \subsection{Method \code{dec()}}{ 126 | \subsection{Usage}{ 127 | \if{html}{\out{
}}\preformatted{MetricInterface$dec(val = 1, ...)}\if{html}{\out{
}} 128 | } 129 | 130 | \subsection{Arguments}{ 131 | \if{html}{\out{
}} 132 | \describe{ 133 | \item{\code{val}}{Value to decrease the metric.} 134 | 135 | \item{\code{...}}{Key value pairs of labels.} 136 | } 137 | \if{html}{\out{
}} 138 | } 139 | \subsection{Details}{ 140 | Decrease the metric to a current value given labels. 141 | } 142 | 143 | } 144 | \if{html}{\out{
}} 145 | \if{html}{\out{}} 146 | \if{latex}{\out{\hypertarget{method-render}{}}} 147 | \subsection{Method \code{render()}}{ 148 | \subsection{Usage}{ 149 | \if{html}{\out{
}}\preformatted{MetricInterface$render()}\if{html}{\out{
}} 150 | } 151 | 152 | \subsection{Details}{ 153 | Render the metric 154 | } 155 | 156 | } 157 | \if{html}{\out{
}} 158 | \if{html}{\out{}} 159 | \if{latex}{\out{\hypertarget{method-clone}{}}} 160 | \subsection{Method \code{clone()}}{ 161 | The objects of this class are cloneable with this method. 162 | \subsection{Usage}{ 163 | \if{html}{\out{
}}\preformatted{MetricInterface$clone(deep = FALSE)}\if{html}{\out{
}} 164 | } 165 | 166 | \subsection{Arguments}{ 167 | \if{html}{\out{
}} 168 | \describe{ 169 | \item{\code{deep}}{Whether to make a deep clone.} 170 | } 171 | \if{html}{\out{
}} 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards 42 | of acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies 54 | when an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail 56 | address, posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at [INSERT CONTACT 63 | METHOD]. All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series of 85 | actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or permanent 92 | ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within the 112 | community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.0, 118 | available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 119 | 120 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 121 | enforcement ladder](https://github.com/mozilla/diversity). 122 | 123 | [homepage]: https://www.contributor-covenant.org 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | https://www.contributor-covenant.org/faq. Translations are available at https:// 127 | www.contributor-covenant.org/translations. 128 | -------------------------------------------------------------------------------- /docs/guide/shiny.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Shiny 3 | summary: How to use titan with shiny applications. 4 | authors: 5 | - John Coene 6 | --- 7 | 8 | # Shiny Basics 9 | 10 | In this wee vignette we give just basic examples of how to use titan in shiny applications. 11 | 12 | ## Helpers 13 | 14 | Titan allows easily tracking some interactions by default, saving you the trouble of setting up metrics. 15 | 16 | Starting from the (very) basic shiny application below which simply prints text to the console at the click of a button. 17 | 18 | ```r 19 | library(shiny) 20 | 21 | ui <- fluidPage( 22 | actionButton("click", "click me!") 23 | ) 24 | 25 | server <- function(input, output){ 26 | observeEvent(input$click, { 27 | cat("Logging one click\n") 28 | }) 29 | } 30 | 31 | shinyApp(ui, server) 32 | ``` 33 | 34 | We will see how to define custom metrics later but before we do so, there are a number out-of-the-box (optional) metrics that titan provides: 35 | 36 | 1. Inputs: Tracks all input messages sent from the front-end to the server to better understand which are most used. This is handled with a counter that uses the `name` of the inputs as `labels`. 37 | 2. Visits: Tracks the total number of visits to the shiny application with a counter. 38 | 3. Concurrent: Tracks the number of concurrent users currently on the application with a gauge. 39 | 4. Duration: Tracks the session duration; the time (in seconds) users stay and interact with the application. 40 | 41 | As mentioned, all of those are optional (off by default) but whether you use the defaults presented here and/or your custom metrics you __must use__ `titanApp` to launch the application. 42 | 43 | This function takes the same inputs as `shinyApp` and more. The arguments `inputs`, `visits`, `concurrent`, and `duration`, which all default to `NULL` meaning they are not being tracked. To track those metrics one must pass it a character string defining the name of the metric. 44 | 45 | ```r hl_lines="1 15" 46 | library(titan) 47 | library(shiny) 48 | 49 | ui <- fluidPage( 50 | actionButton("click", "click me!") 51 | ) 52 | 53 | server <- function(input, output){ 54 | observeEvent(input$click, { 55 | cat("Logging one click\n") 56 | }) 57 | } 58 | 59 | # use titanApp 60 | titanApp( 61 | ui, server, 62 | inputs = "inputs", 63 | visits = "visits", 64 | concurrent = "concurrent", 65 | duration = "duration" 66 | ) 67 | ``` 68 | 69 | !!! danger 70 | On a large application tracking all inputs can create a lot of data. 71 | 72 | ## Counter 73 | 74 | Let's use titan to track the number of clicks on this button rather than use the defaults provided by titan. We can use a counter since the number of clicks can only increase over time. 75 | 76 | Load the titan package then create the counter. Note that it is created outside the application, as it only needs to be created once, placing it in the `server` would recreate it every time. Though this should not be an issue as titan prevents you from overwriting an already created counter it is best to avoid it. 77 | 78 | In the observer we increment the counter, then note that we launch the application with `titanApp` and not `shinyApp`, it works the exact same way but exposes the metrics. 79 | 80 | ```r 81 | library(titan) 82 | library(shiny) 83 | 84 | # create the counter 85 | c <- Counter$new( 86 | name = "btn_click_total", 87 | help = "Number of clicks of the button" 88 | ) 89 | 90 | ui <- fluidPage( 91 | actionButton("click", "click me!") 92 | ) 93 | 94 | server <- function(input, output){ 95 | observeEvent(input$click, { 96 | c$inc() 97 | cat("Logging one click\n") 98 | }) 99 | } 100 | 101 | # use titanApp 102 | titanApp(ui, server) 103 | ``` 104 | 105 | ![](../images/shiny-basic.png) 106 | 107 | ## Histogram 108 | 109 | We can also use a histogram to track the performances of a particularly long request. 110 | 111 | Say for instance, that the application, at the click of a button, makes a relatively large request to a database or runs a time consuming model, surely we'd like to track that. 112 | 113 | We can build a histogram to track the time it takes to run that process. We'll use the histogram to put the time it takes into three bins: 114 | 115 | - Less than 3 seconds 116 | - Between 3 and 6 seconds 117 | - 6 Seconds and more 118 | 119 | ```r 120 | library(titan) 121 | library(shiny) 122 | 123 | classify <- function(value){ 124 | v <- as.numeric(value) 125 | 126 | if(v < 3) 127 | return(bucket("0-3", v)) 128 | else if (v > 3 && v < 6) 129 | return(bucket("3-6", v)) 130 | else 131 | return(bucket("9", v)) 132 | } 133 | 134 | hist <- Histogram$new( 135 | "process_time", 136 | "Lengthy process timing", 137 | predicate = classify 138 | ) 139 | 140 | ui <- fluidPage( 141 | actionButton("click", "click me!") 142 | ) 143 | 144 | server <- function(input, output){ 145 | observeEvent(input$click, { 146 | start <- Sys.time() 147 | 148 | on.exit({ 149 | diff <- Sys.time() - start 150 | 151 | hist$observe(diff) 152 | }) 153 | 154 | Sys.sleep(sample(1:9, 1)) 155 | 156 | cat("Done with process\n") 157 | }) 158 | } 159 | 160 | titanApp(ui, server) 161 | ``` 162 | 163 | ![](../images/shiny-histogram.png) 164 | 165 | ## Gauge 166 | 167 | You could also create a gauge to track the current number of visitors on the application using a Gauge. 168 | 169 | It's as simple as initialising the Gauge and increasing it by one every time the server fires. 170 | 171 | ```r 172 | library(shiny) 173 | 174 | g <- Gauge$new( 175 | "visitors_total", 176 | "Current number of visitors" 177 | ) 178 | 179 | ui <- fluidPage( 180 | h1("Hello") 181 | ) 182 | 183 | server <- function(input, output){ 184 | g$inc() 185 | } 186 | 187 | titanApp(ui, server) 188 | ``` 189 | -------------------------------------------------------------------------------- /site/assets/javascripts/lunr/min/lunr.nl.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Lunr languages, `Dutch` language 3 | * https://github.com/MihaiValentin/lunr-languages 4 | * 5 | * Copyright 2014, Mihai Valentin 6 | * http://www.mozilla.org/MPL/ 7 | */ 8 | /*! 9 | * based on 10 | * Snowball JavaScript Library v0.3 11 | * http://code.google.com/p/urim/ 12 | * http://snowball.tartarus.org/ 13 | * 14 | * Copyright 2010, Oleg Mazko 15 | * http://www.mozilla.org/MPL/ 16 | */ 17 | 18 | !function(r,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e():e()(r.lunr)}(this,function(){return function(r){if(void 0===r)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===r.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");r.nl=function(){this.pipeline.reset(),this.pipeline.add(r.nl.trimmer,r.nl.stopWordFilter,r.nl.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(r.nl.stemmer))},r.nl.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",r.nl.trimmer=r.trimmerSupport.generateTrimmer(r.nl.wordCharacters),r.Pipeline.registerFunction(r.nl.trimmer,"trimmer-nl"),r.nl.stemmer=function(){var e=r.stemmerSupport.Among,i=r.stemmerSupport.SnowballProgram,n=new function(){function r(){for(var r,e,i,o=C.cursor;;){if(C.bra=C.cursor,r=C.find_among(b,11))switch(C.ket=C.cursor,r){case 1:C.slice_from("a");continue;case 2:C.slice_from("e");continue;case 3:C.slice_from("i");continue;case 4:C.slice_from("o");continue;case 5:C.slice_from("u");continue;case 6:if(C.cursor>=C.limit)break;C.cursor++;continue}break}for(C.cursor=o,C.bra=o,C.eq_s(1,"y")?(C.ket=C.cursor,C.slice_from("Y")):C.cursor=o;;)if(e=C.cursor,C.in_grouping(q,97,232)){if(i=C.cursor,C.bra=i,C.eq_s(1,"i"))C.ket=C.cursor,C.in_grouping(q,97,232)&&(C.slice_from("I"),C.cursor=e);else if(C.cursor=i,C.eq_s(1,"y"))C.ket=C.cursor,C.slice_from("Y"),C.cursor=e;else if(n(e))break}else if(n(e))break}function n(r){return C.cursor=r,r>=C.limit||(C.cursor++,!1)}function o(){_=C.limit,d=_,t()||(_=C.cursor,_<3&&(_=3),t()||(d=C.cursor))}function t(){for(;!C.in_grouping(q,97,232);){if(C.cursor>=C.limit)return!0;C.cursor++}for(;!C.out_grouping(q,97,232);){if(C.cursor>=C.limit)return!0;C.cursor++}return!1}function s(){for(var r;;)if(C.bra=C.cursor,r=C.find_among(p,3))switch(C.ket=C.cursor,r){case 1:C.slice_from("y");break;case 2:C.slice_from("i");break;case 3:if(C.cursor>=C.limit)return;C.cursor++}}function u(){return _<=C.cursor}function c(){return d<=C.cursor}function a(){var r=C.limit-C.cursor;C.find_among_b(g,3)&&(C.cursor=C.limit-r,C.ket=C.cursor,C.cursor>C.limit_backward&&(C.cursor--,C.bra=C.cursor,C.slice_del()))}function l(){var r;w=!1,C.ket=C.cursor,C.eq_s_b(1,"e")&&(C.bra=C.cursor,u()&&(r=C.limit-C.cursor,C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-r,C.slice_del(),w=!0,a())))}function m(){var r;u()&&(r=C.limit-C.cursor,C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-r,C.eq_s_b(3,"gem")||(C.cursor=C.limit-r,C.slice_del(),a())))}function f(){var r,e,i,n,o,t,s=C.limit-C.cursor;if(C.ket=C.cursor,r=C.find_among_b(h,5))switch(C.bra=C.cursor,r){case 1:u()&&C.slice_from("heid");break;case 2:m();break;case 3:u()&&C.out_grouping_b(j,97,232)&&C.slice_del()}if(C.cursor=C.limit-s,l(),C.cursor=C.limit-s,C.ket=C.cursor,C.eq_s_b(4,"heid")&&(C.bra=C.cursor,c()&&(e=C.limit-C.cursor,C.eq_s_b(1,"c")||(C.cursor=C.limit-e,C.slice_del(),C.ket=C.cursor,C.eq_s_b(2,"en")&&(C.bra=C.cursor,m())))),C.cursor=C.limit-s,C.ket=C.cursor,r=C.find_among_b(k,6))switch(C.bra=C.cursor,r){case 1:if(c()){if(C.slice_del(),i=C.limit-C.cursor,C.ket=C.cursor,C.eq_s_b(2,"ig")&&(C.bra=C.cursor,c()&&(n=C.limit-C.cursor,!C.eq_s_b(1,"e")))){C.cursor=C.limit-n,C.slice_del();break}C.cursor=C.limit-i,a()}break;case 2:c()&&(o=C.limit-C.cursor,C.eq_s_b(1,"e")||(C.cursor=C.limit-o,C.slice_del()));break;case 3:c()&&(C.slice_del(),l());break;case 4:c()&&C.slice_del();break;case 5:c()&&w&&C.slice_del()}C.cursor=C.limit-s,C.out_grouping_b(z,73,232)&&(t=C.limit-C.cursor,C.find_among_b(v,4)&&C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-t,C.ket=C.cursor,C.cursor>C.limit_backward&&(C.cursor--,C.bra=C.cursor,C.slice_del())))}var d,_,w,b=[new e("",-1,6),new e("á",0,1),new e("ä",0,1),new e("é",0,2),new e("ë",0,2),new e("í",0,3),new e("ï",0,3),new e("ó",0,4),new e("ö",0,4),new e("ú",0,5),new e("ü",0,5)],p=[new e("",-1,3),new e("I",0,2),new e("Y",0,1)],g=[new e("dd",-1,-1),new e("kk",-1,-1),new e("tt",-1,-1)],h=[new e("ene",-1,2),new e("se",-1,3),new e("en",-1,2),new e("heden",2,1),new e("s",-1,3)],k=[new e("end",-1,1),new e("ig",-1,2),new e("ing",-1,1),new e("lijk",-1,3),new e("baar",-1,4),new e("bar",-1,5)],v=[new e("aa",-1,-1),new e("ee",-1,-1),new e("oo",-1,-1),new e("uu",-1,-1)],q=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],z=[1,0,0,17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],j=[17,67,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],C=new i;this.setCurrent=function(r){C.setCurrent(r)},this.getCurrent=function(){return C.getCurrent()},this.stem=function(){var e=C.cursor;return r(),C.cursor=e,o(),C.limit_backward=e,C.cursor=C.limit,f(),C.cursor=C.limit_backward,s(),!0}};return function(r){return"function"==typeof r.update?r.update(function(r){return n.setCurrent(r),n.stem(),n.getCurrent()}):(n.setCurrent(r),n.stem(),n.getCurrent())}}(),r.Pipeline.registerFunction(r.nl.stemmer,"stemmer-nl"),r.nl.stopWordFilter=r.generateStopWordFilter(" aan al alles als altijd andere ben bij daar dan dat de der deze die dit doch doen door dus een eens en er ge geen geweest haar had heb hebben heeft hem het hier hij hoe hun iemand iets ik in is ja je kan kon kunnen maar me meer men met mij mijn moet na naar niet niets nog nu of om omdat onder ons ook op over reeds te tegen toch toen tot u uit uw van veel voor want waren was wat werd wezen wie wil worden wordt zal ze zelf zich zij zijn zo zonder zou".split(" ")),r.Pipeline.registerFunction(r.nl.stopWordFilter,"stopWordFilter-nl")}}); -------------------------------------------------------------------------------- /docs/meta/contribute.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CoC 3 | summary: Contributor covenant code of conduct. 4 | --- 5 | 6 | # Contributor Covenant Code of Conduct 7 | 8 | ## Our Pledge 9 | 10 | We as members, contributors, and leaders pledge to make participation in our 11 | community a harassment-free experience for everyone, regardless of age, body 12 | size, visible or invisible disability, ethnicity, sex characteristics, gender 13 | identity and expression, level of experience, education, socio-economic status, 14 | nationality, personal appearance, race, religion, or sexual identity and 15 | orientation. 16 | 17 | We pledge to act and interact in ways that contribute to an open, welcoming, 18 | diverse, inclusive, and healthy community. 19 | 20 | ## Our Standards 21 | 22 | Examples of behavior that contributes to a positive environment for our 23 | community include: 24 | 25 | * Demonstrating empathy and kindness toward other people 26 | * Being respectful of differing opinions, viewpoints, and experiences 27 | * Giving and gracefully accepting constructive feedback 28 | * Accepting responsibility and apologizing to those affected by our mistakes, 29 | and learning from the experience 30 | * Focusing on what is best not just for us as individuals, but for the overall 31 | community 32 | 33 | Examples of unacceptable behavior include: 34 | 35 | * The use of sexualized language or imagery, and sexual attention or 36 | advances of any kind 37 | * Trolling, insulting or derogatory comments, and personal or political attacks 38 | * Public or private harassment 39 | * Publishing others' private information, such as a physical or email 40 | address, without their explicit permission 41 | * Other conduct which could reasonably be considered inappropriate in a 42 | professional setting 43 | 44 | ## Enforcement Responsibilities 45 | 46 | Community leaders are responsible for clarifying and enforcing our standards 47 | of acceptable behavior and will take appropriate and fair corrective action in 48 | response to any behavior that they deem inappropriate, threatening, offensive, 49 | or harmful. 50 | 51 | Community leaders have the right and responsibility to remove, edit, or reject 52 | comments, commits, code, wiki edits, issues, and other contributions that are 53 | not aligned to this Code of Conduct, and will communicate reasons for moderation 54 | decisions when appropriate. 55 | 56 | ## Scope 57 | 58 | This Code of Conduct applies within all community spaces, and also applies 59 | when an individual is officially representing the community in public spaces. 60 | Examples of representing our community include using an official e-mail 61 | address, posting via an official social media account, or acting as an appointed 62 | representative at an online or offline event. 63 | 64 | ## Enforcement 65 | 66 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 67 | reported to the community leaders responsible for enforcement at [INSERT CONTACT 68 | METHOD]. All complaints will be reviewed and investigated promptly and fairly. 69 | 70 | All community leaders are obligated to respect the privacy and security of the 71 | reporter of any incident. 72 | 73 | ## Enforcement Guidelines 74 | 75 | Community leaders will follow these Community Impact Guidelines in determining 76 | the consequences for any action they deem in violation of this Code of Conduct: 77 | 78 | ### 1. Correction 79 | 80 | **Community Impact**: Use of inappropriate language or other behavior deemed 81 | unprofessional or unwelcome in the community. 82 | 83 | **Consequence**: A private, written warning from community leaders, providing 84 | clarity around the nature of the violation and an explanation of why the 85 | behavior was inappropriate. A public apology may be requested. 86 | 87 | ### 2. Warning 88 | 89 | **Community Impact**: A violation through a single incident or series of 90 | actions. 91 | 92 | **Consequence**: A warning with consequences for continued behavior. No 93 | interaction with the people involved, including unsolicited interaction with 94 | those enforcing the Code of Conduct, for a specified period of time. This 95 | includes avoiding interactions in community spaces as well as external channels 96 | like social media. Violating these terms may lead to a temporary or permanent 97 | ban. 98 | 99 | ### 3. Temporary Ban 100 | 101 | **Community Impact**: A serious violation of community standards, including 102 | sustained inappropriate behavior. 103 | 104 | **Consequence**: A temporary ban from any sort of interaction or public 105 | communication with the community for a specified period of time. No public or 106 | private interaction with the people involved, including unsolicited interaction 107 | with those enforcing the Code of Conduct, is allowed during this period. 108 | Violating these terms may lead to a permanent ban. 109 | 110 | ### 4. Permanent Ban 111 | 112 | **Community Impact**: Demonstrating a pattern of violation of community 113 | standards, including sustained inappropriate behavior, harassment of an 114 | individual, or aggression toward or disparagement of classes of individuals. 115 | 116 | **Consequence**: A permanent ban from any sort of public interaction within the 117 | community. 118 | 119 | ## Attribution 120 | 121 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 122 | version 2.0, 123 | available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 124 | 125 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 126 | enforcement ladder](https://github.com/mozilla/diversity). 127 | 128 | [homepage]: https://www.contributor-covenant.org 129 | 130 | For answers to common questions about this code of conduct, see the FAQ at 131 | https://www.contributor-covenant.org/faq. Translations are available at https:// 132 | www.contributor-covenant.org/translations. 133 | -------------------------------------------------------------------------------- /site/assets/javascripts/lunr/min/lunr.de.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Lunr languages, `German` language 3 | * https://github.com/MihaiValentin/lunr-languages 4 | * 5 | * Copyright 2014, Mihai Valentin 6 | * http://www.mozilla.org/MPL/ 7 | */ 8 | /*! 9 | * based on 10 | * Snowball JavaScript Library v0.3 11 | * http://code.google.com/p/urim/ 12 | * http://snowball.tartarus.org/ 13 | * 14 | * Copyright 2010, Oleg Mazko 15 | * http://www.mozilla.org/MPL/ 16 | */ 17 | 18 | !function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.de=function(){this.pipeline.reset(),this.pipeline.add(e.de.trimmer,e.de.stopWordFilter,e.de.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.de.stemmer))},e.de.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.de.trimmer=e.trimmerSupport.generateTrimmer(e.de.wordCharacters),e.Pipeline.registerFunction(e.de.trimmer,"trimmer-de"),e.de.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){function e(e,r,n){return!(!v.eq_s(1,e)||(v.ket=v.cursor,!v.in_grouping(p,97,252)))&&(v.slice_from(r),v.cursor=n,!0)}function i(){for(var r,n,i,s,t=v.cursor;;)if(r=v.cursor,v.bra=r,v.eq_s(1,"ß"))v.ket=v.cursor,v.slice_from("ss");else{if(r>=v.limit)break;v.cursor=r+1}for(v.cursor=t;;)for(n=v.cursor;;){if(i=v.cursor,v.in_grouping(p,97,252)){if(s=v.cursor,v.bra=s,e("u","U",i))break;if(v.cursor=s,e("y","Y",i))break}if(i>=v.limit)return void(v.cursor=n);v.cursor=i+1}}function s(){for(;!v.in_grouping(p,97,252);){if(v.cursor>=v.limit)return!0;v.cursor++}for(;!v.out_grouping(p,97,252);){if(v.cursor>=v.limit)return!0;v.cursor++}return!1}function t(){m=v.limit,l=m;var e=v.cursor+3;0<=e&&e<=v.limit&&(d=e,s()||(m=v.cursor,m=v.limit)return;v.cursor++}}}function c(){return m<=v.cursor}function u(){return l<=v.cursor}function a(){var e,r,n,i,s=v.limit-v.cursor;if(v.ket=v.cursor,(e=v.find_among_b(w,7))&&(v.bra=v.cursor,c()))switch(e){case 1:v.slice_del();break;case 2:v.slice_del(),v.ket=v.cursor,v.eq_s_b(1,"s")&&(v.bra=v.cursor,v.eq_s_b(3,"nis")&&v.slice_del());break;case 3:v.in_grouping_b(g,98,116)&&v.slice_del()}if(v.cursor=v.limit-s,v.ket=v.cursor,(e=v.find_among_b(f,4))&&(v.bra=v.cursor,c()))switch(e){case 1:v.slice_del();break;case 2:if(v.in_grouping_b(k,98,116)){var t=v.cursor-3;v.limit_backward<=t&&t<=v.limit&&(v.cursor=t,v.slice_del())}}if(v.cursor=v.limit-s,v.ket=v.cursor,(e=v.find_among_b(_,8))&&(v.bra=v.cursor,u()))switch(e){case 1:v.slice_del(),v.ket=v.cursor,v.eq_s_b(2,"ig")&&(v.bra=v.cursor,r=v.limit-v.cursor,v.eq_s_b(1,"e")||(v.cursor=v.limit-r,u()&&v.slice_del()));break;case 2:n=v.limit-v.cursor,v.eq_s_b(1,"e")||(v.cursor=v.limit-n,v.slice_del());break;case 3:if(v.slice_del(),v.ket=v.cursor,i=v.limit-v.cursor,!v.eq_s_b(2,"er")&&(v.cursor=v.limit-i,!v.eq_s_b(2,"en")))break;v.bra=v.cursor,c()&&v.slice_del();break;case 4:v.slice_del(),v.ket=v.cursor,e=v.find_among_b(b,2),e&&(v.bra=v.cursor,u()&&1==e&&v.slice_del())}}var d,l,m,h=[new r("",-1,6),new r("U",0,2),new r("Y",0,1),new r("ä",0,3),new r("ö",0,4),new r("ü",0,5)],w=[new r("e",-1,2),new r("em",-1,1),new r("en",-1,2),new r("ern",-1,1),new r("er",-1,1),new r("s",-1,3),new r("es",5,2)],f=[new r("en",-1,1),new r("er",-1,1),new r("st",-1,2),new r("est",2,1)],b=[new r("ig",-1,1),new r("lich",-1,1)],_=[new r("end",-1,1),new r("ig",-1,2),new r("ung",-1,1),new r("lich",-1,3),new r("isch",-1,2),new r("ik",-1,2),new r("heit",-1,3),new r("keit",-1,4)],p=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32,8],g=[117,30,5],k=[117,30,4],v=new n;this.setCurrent=function(e){v.setCurrent(e)},this.getCurrent=function(){return v.getCurrent()},this.stem=function(){var e=v.cursor;return i(),v.cursor=e,t(),v.limit_backward=e,v.cursor=v.limit,a(),v.cursor=v.limit_backward,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.de.stemmer,"stemmer-de"),e.de.stopWordFilter=e.generateStopWordFilter("aber alle allem allen aller alles als also am an ander andere anderem anderen anderer anderes anderm andern anderr anders auch auf aus bei bin bis bist da damit dann das dasselbe dazu daß dein deine deinem deinen deiner deines dem demselben den denn denselben der derer derselbe derselben des desselben dessen dich die dies diese dieselbe dieselben diesem diesen dieser dieses dir doch dort du durch ein eine einem einen einer eines einig einige einigem einigen einiger einiges einmal er es etwas euch euer eure eurem euren eurer eures für gegen gewesen hab habe haben hat hatte hatten hier hin hinter ich ihm ihn ihnen ihr ihre ihrem ihren ihrer ihres im in indem ins ist jede jedem jeden jeder jedes jene jenem jenen jener jenes jetzt kann kein keine keinem keinen keiner keines können könnte machen man manche manchem manchen mancher manches mein meine meinem meinen meiner meines mich mir mit muss musste nach nicht nichts noch nun nur ob oder ohne sehr sein seine seinem seinen seiner seines selbst sich sie sind so solche solchem solchen solcher solches soll sollte sondern sonst um und uns unse unsem unsen unser unses unter viel vom von vor war waren warst was weg weil weiter welche welchem welchen welcher welches wenn werde werden wie wieder will wir wird wirst wo wollen wollte während würde würden zu zum zur zwar zwischen über".split(" ")),e.Pipeline.registerFunction(e.de.stopWordFilter,"stopWordFilter-de")}}); -------------------------------------------------------------------------------- /site/assets/javascripts/lunr/min/lunr.du.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Lunr languages, `Dutch` language 3 | * https://github.com/MihaiValentin/lunr-languages 4 | * 5 | * Copyright 2014, Mihai Valentin 6 | * http://www.mozilla.org/MPL/ 7 | */ 8 | /*! 9 | * based on 10 | * Snowball JavaScript Library v0.3 11 | * http://code.google.com/p/urim/ 12 | * http://snowball.tartarus.org/ 13 | * 14 | * Copyright 2010, Oleg Mazko 15 | * http://www.mozilla.org/MPL/ 16 | */ 17 | 18 | !function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");console.warn('[Lunr Languages] Please use the "nl" instead of the "du". The "nl" code is the standard code for Dutch language, and "du" will be removed in the next major versions.'),e.du=function(){this.pipeline.reset(),this.pipeline.add(e.du.trimmer,e.du.stopWordFilter,e.du.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.du.stemmer))},e.du.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.du.trimmer=e.trimmerSupport.generateTrimmer(e.du.wordCharacters),e.Pipeline.registerFunction(e.du.trimmer,"trimmer-du"),e.du.stemmer=function(){var r=e.stemmerSupport.Among,i=e.stemmerSupport.SnowballProgram,n=new function(){function e(){for(var e,r,i,o=C.cursor;;){if(C.bra=C.cursor,e=C.find_among(b,11))switch(C.ket=C.cursor,e){case 1:C.slice_from("a");continue;case 2:C.slice_from("e");continue;case 3:C.slice_from("i");continue;case 4:C.slice_from("o");continue;case 5:C.slice_from("u");continue;case 6:if(C.cursor>=C.limit)break;C.cursor++;continue}break}for(C.cursor=o,C.bra=o,C.eq_s(1,"y")?(C.ket=C.cursor,C.slice_from("Y")):C.cursor=o;;)if(r=C.cursor,C.in_grouping(q,97,232)){if(i=C.cursor,C.bra=i,C.eq_s(1,"i"))C.ket=C.cursor,C.in_grouping(q,97,232)&&(C.slice_from("I"),C.cursor=r);else if(C.cursor=i,C.eq_s(1,"y"))C.ket=C.cursor,C.slice_from("Y"),C.cursor=r;else if(n(r))break}else if(n(r))break}function n(e){return C.cursor=e,e>=C.limit||(C.cursor++,!1)}function o(){_=C.limit,f=_,t()||(_=C.cursor,_<3&&(_=3),t()||(f=C.cursor))}function t(){for(;!C.in_grouping(q,97,232);){if(C.cursor>=C.limit)return!0;C.cursor++}for(;!C.out_grouping(q,97,232);){if(C.cursor>=C.limit)return!0;C.cursor++}return!1}function s(){for(var e;;)if(C.bra=C.cursor,e=C.find_among(p,3))switch(C.ket=C.cursor,e){case 1:C.slice_from("y");break;case 2:C.slice_from("i");break;case 3:if(C.cursor>=C.limit)return;C.cursor++}}function u(){return _<=C.cursor}function c(){return f<=C.cursor}function a(){var e=C.limit-C.cursor;C.find_among_b(g,3)&&(C.cursor=C.limit-e,C.ket=C.cursor,C.cursor>C.limit_backward&&(C.cursor--,C.bra=C.cursor,C.slice_del()))}function l(){var e;w=!1,C.ket=C.cursor,C.eq_s_b(1,"e")&&(C.bra=C.cursor,u()&&(e=C.limit-C.cursor,C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-e,C.slice_del(),w=!0,a())))}function m(){var e;u()&&(e=C.limit-C.cursor,C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-e,C.eq_s_b(3,"gem")||(C.cursor=C.limit-e,C.slice_del(),a())))}function d(){var e,r,i,n,o,t,s=C.limit-C.cursor;if(C.ket=C.cursor,e=C.find_among_b(h,5))switch(C.bra=C.cursor,e){case 1:u()&&C.slice_from("heid");break;case 2:m();break;case 3:u()&&C.out_grouping_b(z,97,232)&&C.slice_del()}if(C.cursor=C.limit-s,l(),C.cursor=C.limit-s,C.ket=C.cursor,C.eq_s_b(4,"heid")&&(C.bra=C.cursor,c()&&(r=C.limit-C.cursor,C.eq_s_b(1,"c")||(C.cursor=C.limit-r,C.slice_del(),C.ket=C.cursor,C.eq_s_b(2,"en")&&(C.bra=C.cursor,m())))),C.cursor=C.limit-s,C.ket=C.cursor,e=C.find_among_b(k,6))switch(C.bra=C.cursor,e){case 1:if(c()){if(C.slice_del(),i=C.limit-C.cursor,C.ket=C.cursor,C.eq_s_b(2,"ig")&&(C.bra=C.cursor,c()&&(n=C.limit-C.cursor,!C.eq_s_b(1,"e")))){C.cursor=C.limit-n,C.slice_del();break}C.cursor=C.limit-i,a()}break;case 2:c()&&(o=C.limit-C.cursor,C.eq_s_b(1,"e")||(C.cursor=C.limit-o,C.slice_del()));break;case 3:c()&&(C.slice_del(),l());break;case 4:c()&&C.slice_del();break;case 5:c()&&w&&C.slice_del()}C.cursor=C.limit-s,C.out_grouping_b(j,73,232)&&(t=C.limit-C.cursor,C.find_among_b(v,4)&&C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-t,C.ket=C.cursor,C.cursor>C.limit_backward&&(C.cursor--,C.bra=C.cursor,C.slice_del())))}var f,_,w,b=[new r("",-1,6),new r("á",0,1),new r("ä",0,1),new r("é",0,2),new r("ë",0,2),new r("í",0,3),new r("ï",0,3),new r("ó",0,4),new r("ö",0,4),new r("ú",0,5),new r("ü",0,5)],p=[new r("",-1,3),new r("I",0,2),new r("Y",0,1)],g=[new r("dd",-1,-1),new r("kk",-1,-1),new r("tt",-1,-1)],h=[new r("ene",-1,2),new r("se",-1,3),new r("en",-1,2),new r("heden",2,1),new r("s",-1,3)],k=[new r("end",-1,1),new r("ig",-1,2),new r("ing",-1,1),new r("lijk",-1,3),new r("baar",-1,4),new r("bar",-1,5)],v=[new r("aa",-1,-1),new r("ee",-1,-1),new r("oo",-1,-1),new r("uu",-1,-1)],q=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],j=[1,0,0,17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],z=[17,67,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],C=new i;this.setCurrent=function(e){C.setCurrent(e)},this.getCurrent=function(){return C.getCurrent()},this.stem=function(){var r=C.cursor;return e(),C.cursor=r,o(),C.limit_backward=r,C.cursor=C.limit,d(),C.cursor=C.limit_backward,s(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.du.stemmer,"stemmer-du"),e.du.stopWordFilter=e.generateStopWordFilter(" aan al alles als altijd andere ben bij daar dan dat de der deze die dit doch doen door dus een eens en er ge geen geweest haar had heb hebben heeft hem het hier hij hoe hun iemand iets ik in is ja je kan kon kunnen maar me meer men met mij mijn moet na naar niet niets nog nu of om omdat onder ons ook op over reeds te tegen toch toen tot u uit uw van veel voor want waren was wat werd wezen wie wil worden wordt zal ze zelf zich zij zijn zo zonder zou".split(" ")),e.Pipeline.registerFunction(e.du.stopWordFilter,"stopWordFilter-du")}}); -------------------------------------------------------------------------------- /site/assets/javascripts/lunr/min/lunr.ru.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Lunr languages, `Russian` language 3 | * https://github.com/MihaiValentin/lunr-languages 4 | * 5 | * Copyright 2014, Mihai Valentin 6 | * http://www.mozilla.org/MPL/ 7 | */ 8 | /*! 9 | * based on 10 | * Snowball JavaScript Library v0.3 11 | * http://code.google.com/p/urim/ 12 | * http://snowball.tartarus.org/ 13 | * 14 | * Copyright 2010, Oleg Mazko 15 | * http://www.mozilla.org/MPL/ 16 | */ 17 | 18 | !function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():n()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.ru=function(){this.pipeline.reset(),this.pipeline.add(e.ru.trimmer,e.ru.stopWordFilter,e.ru.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ru.stemmer))},e.ru.wordCharacters="Ѐ-҄҇-ԯᴫᵸⷠ-ⷿꙀ-ꚟ︮︯",e.ru.trimmer=e.trimmerSupport.generateTrimmer(e.ru.wordCharacters),e.Pipeline.registerFunction(e.ru.trimmer,"trimmer-ru"),e.ru.stemmer=function(){var n=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,t=new function(){function e(){for(;!W.in_grouping(S,1072,1103);){if(W.cursor>=W.limit)return!1;W.cursor++}return!0}function t(){for(;!W.out_grouping(S,1072,1103);){if(W.cursor>=W.limit)return!1;W.cursor++}return!0}function w(){b=W.limit,_=b,e()&&(b=W.cursor,t()&&e()&&t()&&(_=W.cursor))}function i(){return _<=W.cursor}function u(e,n){var r,t;if(W.ket=W.cursor,r=W.find_among_b(e,n)){switch(W.bra=W.cursor,r){case 1:if(t=W.limit-W.cursor,!W.eq_s_b(1,"а")&&(W.cursor=W.limit-t,!W.eq_s_b(1,"я")))return!1;case 2:W.slice_del()}return!0}return!1}function o(){return u(h,9)}function s(e,n){var r;return W.ket=W.cursor,!!(r=W.find_among_b(e,n))&&(W.bra=W.cursor,1==r&&W.slice_del(),!0)}function c(){return s(g,26)}function m(){return!!c()&&(u(C,8),!0)}function f(){return s(k,2)}function l(){return u(P,46)}function a(){s(v,36)}function p(){var e;W.ket=W.cursor,(e=W.find_among_b(F,2))&&(W.bra=W.cursor,i()&&1==e&&W.slice_del())}function d(){var e;if(W.ket=W.cursor,e=W.find_among_b(q,4))switch(W.bra=W.cursor,e){case 1:if(W.slice_del(),W.ket=W.cursor,!W.eq_s_b(1,"н"))break;W.bra=W.cursor;case 2:if(!W.eq_s_b(1,"н"))break;case 3:W.slice_del()}}var _,b,h=[new n("в",-1,1),new n("ив",0,2),new n("ыв",0,2),new n("вши",-1,1),new n("ивши",3,2),new n("ывши",3,2),new n("вшись",-1,1),new n("ившись",6,2),new n("ывшись",6,2)],g=[new n("ее",-1,1),new n("ие",-1,1),new n("ое",-1,1),new n("ые",-1,1),new n("ими",-1,1),new n("ыми",-1,1),new n("ей",-1,1),new n("ий",-1,1),new n("ой",-1,1),new n("ый",-1,1),new n("ем",-1,1),new n("им",-1,1),new n("ом",-1,1),new n("ым",-1,1),new n("его",-1,1),new n("ого",-1,1),new n("ему",-1,1),new n("ому",-1,1),new n("их",-1,1),new n("ых",-1,1),new n("ею",-1,1),new n("ою",-1,1),new n("ую",-1,1),new n("юю",-1,1),new n("ая",-1,1),new n("яя",-1,1)],C=[new n("ем",-1,1),new n("нн",-1,1),new n("вш",-1,1),new n("ивш",2,2),new n("ывш",2,2),new n("щ",-1,1),new n("ющ",5,1),new n("ующ",6,2)],k=[new n("сь",-1,1),new n("ся",-1,1)],P=[new n("ла",-1,1),new n("ила",0,2),new n("ыла",0,2),new n("на",-1,1),new n("ена",3,2),new n("ете",-1,1),new n("ите",-1,2),new n("йте",-1,1),new n("ейте",7,2),new n("уйте",7,2),new n("ли",-1,1),new n("или",10,2),new n("ыли",10,2),new n("й",-1,1),new n("ей",13,2),new n("уй",13,2),new n("л",-1,1),new n("ил",16,2),new n("ыл",16,2),new n("ем",-1,1),new n("им",-1,2),new n("ым",-1,2),new n("н",-1,1),new n("ен",22,2),new n("ло",-1,1),new n("ило",24,2),new n("ыло",24,2),new n("но",-1,1),new n("ено",27,2),new n("нно",27,1),new n("ет",-1,1),new n("ует",30,2),new n("ит",-1,2),new n("ыт",-1,2),new n("ют",-1,1),new n("уют",34,2),new n("ят",-1,2),new n("ны",-1,1),new n("ены",37,2),new n("ть",-1,1),new n("ить",39,2),new n("ыть",39,2),new n("ешь",-1,1),new n("ишь",-1,2),new n("ю",-1,2),new n("ую",44,2)],v=[new n("а",-1,1),new n("ев",-1,1),new n("ов",-1,1),new n("е",-1,1),new n("ие",3,1),new n("ье",3,1),new n("и",-1,1),new n("еи",6,1),new n("ии",6,1),new n("ами",6,1),new n("ями",6,1),new n("иями",10,1),new n("й",-1,1),new n("ей",12,1),new n("ией",13,1),new n("ий",12,1),new n("ой",12,1),new n("ам",-1,1),new n("ем",-1,1),new n("ием",18,1),new n("ом",-1,1),new n("ям",-1,1),new n("иям",21,1),new n("о",-1,1),new n("у",-1,1),new n("ах",-1,1),new n("ях",-1,1),new n("иях",26,1),new n("ы",-1,1),new n("ь",-1,1),new n("ю",-1,1),new n("ию",30,1),new n("ью",30,1),new n("я",-1,1),new n("ия",33,1),new n("ья",33,1)],F=[new n("ост",-1,1),new n("ость",-1,1)],q=[new n("ейше",-1,1),new n("н",-1,2),new n("ейш",-1,1),new n("ь",-1,3)],S=[33,65,8,232],W=new r;this.setCurrent=function(e){W.setCurrent(e)},this.getCurrent=function(){return W.getCurrent()},this.stem=function(){return w(),W.cursor=W.limit,!(W.cursor=A.limit)return!0;A.cursor++}for(A.cursor=i;!A.out_grouping(W,97,246);){if(A.cursor>=A.limit)return!0;A.cursor++}return!1}function t(){return d<=A.cursor}function s(){var i,e;if(A.cursor>=f)if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,i=A.find_among_b(h,10)){switch(A.bra=A.cursor,A.limit_backward=e,i){case 1:if(!A.in_grouping_b(x,97,246))return;break;case 2:if(!t())return}A.slice_del()}else A.limit_backward=e}function o(){var i,e,r;if(A.cursor>=f)if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,i=A.find_among_b(v,9))switch(A.bra=A.cursor,A.limit_backward=e,i){case 1:r=A.limit-A.cursor,A.eq_s_b(1,"k")||(A.cursor=A.limit-r,A.slice_del());break;case 2:A.slice_del(),A.ket=A.cursor,A.eq_s_b(3,"kse")&&(A.bra=A.cursor,A.slice_from("ksi"));break;case 3:A.slice_del();break;case 4:A.find_among_b(p,6)&&A.slice_del();break;case 5:A.find_among_b(g,6)&&A.slice_del();break;case 6:A.find_among_b(j,2)&&A.slice_del()}else A.limit_backward=e}function l(){return A.find_among_b(q,7)}function a(){return A.eq_s_b(1,"i")&&A.in_grouping_b(L,97,246)}function u(){var i,e,r;if(A.cursor>=f)if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,i=A.find_among_b(C,30)){switch(A.bra=A.cursor,A.limit_backward=e,i){case 1:if(!A.eq_s_b(1,"a"))return;break;case 2:case 9:if(!A.eq_s_b(1,"e"))return;break;case 3:if(!A.eq_s_b(1,"i"))return;break;case 4:if(!A.eq_s_b(1,"o"))return;break;case 5:if(!A.eq_s_b(1,"ä"))return;break;case 6:if(!A.eq_s_b(1,"ö"))return;break;case 7:if(r=A.limit-A.cursor,!l()&&(A.cursor=A.limit-r,!A.eq_s_b(2,"ie"))){A.cursor=A.limit-r;break}if(A.cursor=A.limit-r,A.cursor<=A.limit_backward){A.cursor=A.limit-r;break}A.cursor--,A.bra=A.cursor;break;case 8:if(!A.in_grouping_b(W,97,246)||!A.out_grouping_b(W,97,246))return}A.slice_del(),k=!0}else A.limit_backward=e}function c(){var i,e,r;if(A.cursor>=d)if(e=A.limit_backward,A.limit_backward=d,A.ket=A.cursor,i=A.find_among_b(P,14)){if(A.bra=A.cursor,A.limit_backward=e,1==i){if(r=A.limit-A.cursor,A.eq_s_b(2,"po"))return;A.cursor=A.limit-r}A.slice_del()}else A.limit_backward=e}function m(){var i;A.cursor>=f&&(i=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,A.find_among_b(F,2)?(A.bra=A.cursor,A.limit_backward=i,A.slice_del()):A.limit_backward=i)}function w(){var i,e,r,n,t,s;if(A.cursor>=f){if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,A.eq_s_b(1,"t")&&(A.bra=A.cursor,r=A.limit-A.cursor,A.in_grouping_b(W,97,246)&&(A.cursor=A.limit-r,A.slice_del(),A.limit_backward=e,n=A.limit-A.cursor,A.cursor>=d&&(A.cursor=d,t=A.limit_backward,A.limit_backward=A.cursor,A.cursor=A.limit-n,A.ket=A.cursor,i=A.find_among_b(S,2))))){if(A.bra=A.cursor,A.limit_backward=t,1==i){if(s=A.limit-A.cursor,A.eq_s_b(2,"po"))return;A.cursor=A.limit-s}return void A.slice_del()}A.limit_backward=e}}function _(){var i,e,r,n;if(A.cursor>=f){for(i=A.limit_backward,A.limit_backward=f,e=A.limit-A.cursor,l()&&(A.cursor=A.limit-e,A.ket=A.cursor,A.cursor>A.limit_backward&&(A.cursor--,A.bra=A.cursor,A.slice_del())),A.cursor=A.limit-e,A.ket=A.cursor,A.in_grouping_b(y,97,228)&&(A.bra=A.cursor,A.out_grouping_b(W,97,246)&&A.slice_del()),A.cursor=A.limit-e,A.ket=A.cursor,A.eq_s_b(1,"j")&&(A.bra=A.cursor,r=A.limit-A.cursor,A.eq_s_b(1,"o")?A.slice_del():(A.cursor=A.limit-r,A.eq_s_b(1,"u")&&A.slice_del())),A.cursor=A.limit-e,A.ket=A.cursor,A.eq_s_b(1,"o")&&(A.bra=A.cursor,A.eq_s_b(1,"j")&&A.slice_del()),A.cursor=A.limit-e,A.limit_backward=i;;){if(n=A.limit-A.cursor,A.out_grouping_b(W,97,246)){A.cursor=A.limit-n;break}if(A.cursor=A.limit-n,A.cursor<=A.limit_backward)return;A.cursor--}A.ket=A.cursor,A.cursor>A.limit_backward&&(A.cursor--,A.bra=A.cursor,b=A.slice_to(),A.eq_v_b(b)&&A.slice_del())}}var k,b,d,f,h=[new e("pa",-1,1),new e("sti",-1,2),new e("kaan",-1,1),new e("han",-1,1),new e("kin",-1,1),new e("hän",-1,1),new e("kään",-1,1),new e("ko",-1,1),new e("pä",-1,1),new e("kö",-1,1)],p=[new e("lla",-1,-1),new e("na",-1,-1),new e("ssa",-1,-1),new e("ta",-1,-1),new e("lta",3,-1),new e("sta",3,-1)],g=[new e("llä",-1,-1),new e("nä",-1,-1),new e("ssä",-1,-1),new e("tä",-1,-1),new e("ltä",3,-1),new e("stä",3,-1)],j=[new e("lle",-1,-1),new e("ine",-1,-1)],v=[new e("nsa",-1,3),new e("mme",-1,3),new e("nne",-1,3),new e("ni",-1,2),new e("si",-1,1),new e("an",-1,4),new e("en",-1,6),new e("än",-1,5),new e("nsä",-1,3)],q=[new e("aa",-1,-1),new e("ee",-1,-1),new e("ii",-1,-1),new e("oo",-1,-1),new e("uu",-1,-1),new e("ää",-1,-1),new e("öö",-1,-1)],C=[new e("a",-1,8),new e("lla",0,-1),new e("na",0,-1),new e("ssa",0,-1),new e("ta",0,-1),new e("lta",4,-1),new e("sta",4,-1),new e("tta",4,9),new e("lle",-1,-1),new e("ine",-1,-1),new e("ksi",-1,-1),new e("n",-1,7),new e("han",11,1),new e("den",11,-1,a),new e("seen",11,-1,l),new e("hen",11,2),new e("tten",11,-1,a),new e("hin",11,3),new e("siin",11,-1,a),new e("hon",11,4),new e("hän",11,5),new e("hön",11,6),new e("ä",-1,8),new e("llä",22,-1),new e("nä",22,-1),new e("ssä",22,-1),new e("tä",22,-1),new e("ltä",26,-1),new e("stä",26,-1),new e("ttä",26,9)],P=[new e("eja",-1,-1),new e("mma",-1,1),new e("imma",1,-1),new e("mpa",-1,1),new e("impa",3,-1),new e("mmi",-1,1),new e("immi",5,-1),new e("mpi",-1,1),new e("impi",7,-1),new e("ejä",-1,-1),new e("mmä",-1,1),new e("immä",10,-1),new e("mpä",-1,1),new e("impä",12,-1)],F=[new e("i",-1,-1),new e("j",-1,-1)],S=[new e("mma",-1,1),new e("imma",0,-1)],y=[17,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8],W=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],L=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],x=[17,97,24,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],A=new r;this.setCurrent=function(i){A.setCurrent(i)},this.getCurrent=function(){return A.getCurrent()},this.stem=function(){var e=A.cursor;return i(),k=!1,A.limit_backward=e,A.cursor=A.limit,s(),A.cursor=A.limit,o(),A.cursor=A.limit,u(),A.cursor=A.limit,c(),A.cursor=A.limit,k?(m(),A.cursor=A.limit):(A.cursor=A.limit,w(),A.cursor=A.limit),_(),!0}};return function(i){return"function"==typeof i.update?i.update(function(i){return n.setCurrent(i),n.stem(),n.getCurrent()}):(n.setCurrent(i),n.stem(),n.getCurrent())}}(),i.Pipeline.registerFunction(i.fi.stemmer,"stemmer-fi"),i.fi.stopWordFilter=i.generateStopWordFilter("ei eivät emme en et ette että he heidän heidät heihin heille heillä heiltä heissä heistä heitä hän häneen hänelle hänellä häneltä hänen hänessä hänestä hänet häntä itse ja johon joiden joihin joiksi joilla joille joilta joina joissa joista joita joka joksi jolla jolle jolta jona jonka jos jossa josta jota jotka kanssa keiden keihin keiksi keille keillä keiltä keinä keissä keistä keitä keneen keneksi kenelle kenellä keneltä kenen kenenä kenessä kenestä kenet ketkä ketkä ketä koska kuin kuka kun me meidän meidät meihin meille meillä meiltä meissä meistä meitä mihin miksi mikä mille millä miltä minkä minkä minua minulla minulle minulta minun minussa minusta minut minuun minä minä missä mistä mitkä mitä mukaan mutta ne niiden niihin niiksi niille niillä niiltä niin niin niinä niissä niistä niitä noiden noihin noiksi noilla noille noilta noin noina noissa noista noita nuo nyt näiden näihin näiksi näille näillä näiltä näinä näissä näistä näitä nämä ole olemme olen olet olette oli olimme olin olisi olisimme olisin olisit olisitte olisivat olit olitte olivat olla olleet ollut on ovat poikki se sekä sen siihen siinä siitä siksi sille sillä sillä siltä sinua sinulla sinulle sinulta sinun sinussa sinusta sinut sinuun sinä sinä sitä tai te teidän teidät teihin teille teillä teiltä teissä teistä teitä tuo tuohon tuoksi tuolla tuolle tuolta tuon tuona tuossa tuosta tuota tähän täksi tälle tällä tältä tämä tämän tänä tässä tästä tätä vaan vai vaikka yli".split(" ")),i.Pipeline.registerFunction(i.fi.stopWordFilter,"stopWordFilter-fi")}}); -------------------------------------------------------------------------------- /site/assets/javascripts/lunr/min/lunr.hu.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Lunr languages, `Hungarian` language 3 | * https://github.com/MihaiValentin/lunr-languages 4 | * 5 | * Copyright 2014, Mihai Valentin 6 | * http://www.mozilla.org/MPL/ 7 | */ 8 | /*! 9 | * based on 10 | * Snowball JavaScript Library v0.3 11 | * http://code.google.com/p/urim/ 12 | * http://snowball.tartarus.org/ 13 | * 14 | * Copyright 2010, Oleg Mazko 15 | * http://www.mozilla.org/MPL/ 16 | */ 17 | 18 | !function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():n()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.hu=function(){this.pipeline.reset(),this.pipeline.add(e.hu.trimmer,e.hu.stopWordFilter,e.hu.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.hu.stemmer))},e.hu.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.hu.trimmer=e.trimmerSupport.generateTrimmer(e.hu.wordCharacters),e.Pipeline.registerFunction(e.hu.trimmer,"trimmer-hu"),e.hu.stemmer=function(){var n=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,i=new function(){function e(){var e,n=L.cursor;if(d=L.limit,L.in_grouping(W,97,252))for(;;){if(e=L.cursor,L.out_grouping(W,97,252))return L.cursor=e,L.find_among(g,8)||(L.cursor=e,e=L.limit)return void(d=e);L.cursor++}if(L.cursor=n,L.out_grouping(W,97,252)){for(;!L.in_grouping(W,97,252);){if(L.cursor>=L.limit)return;L.cursor++}d=L.cursor}}function i(){return d<=L.cursor}function a(){var e;if(L.ket=L.cursor,(e=L.find_among_b(h,2))&&(L.bra=L.cursor,i()))switch(e){case 1:L.slice_from("a");break;case 2:L.slice_from("e")}}function t(){var e=L.limit-L.cursor;return!!L.find_among_b(p,23)&&(L.cursor=L.limit-e,!0)}function s(){if(L.cursor>L.limit_backward){L.cursor--,L.ket=L.cursor;var e=L.cursor-1;L.limit_backward<=e&&e<=L.limit&&(L.cursor=e,L.bra=e,L.slice_del())}}function c(){var e;if(L.ket=L.cursor,(e=L.find_among_b(_,2))&&(L.bra=L.cursor,i())){if((1==e||2==e)&&!t())return;L.slice_del(),s()}}function o(){L.ket=L.cursor,L.find_among_b(v,44)&&(L.bra=L.cursor,i()&&(L.slice_del(),a()))}function w(){var e;if(L.ket=L.cursor,(e=L.find_among_b(z,3))&&(L.bra=L.cursor,i()))switch(e){case 1:L.slice_from("e");break;case 2:case 3:L.slice_from("a")}}function l(){var e;if(L.ket=L.cursor,(e=L.find_among_b(y,6))&&(L.bra=L.cursor,i()))switch(e){case 1:case 2:L.slice_del();break;case 3:L.slice_from("a");break;case 4:L.slice_from("e")}}function u(){var e;if(L.ket=L.cursor,(e=L.find_among_b(j,2))&&(L.bra=L.cursor,i())){if((1==e||2==e)&&!t())return;L.slice_del(),s()}}function m(){var e;if(L.ket=L.cursor,(e=L.find_among_b(C,7))&&(L.bra=L.cursor,i()))switch(e){case 1:L.slice_from("a");break;case 2:L.slice_from("e");break;case 3:case 4:case 5:case 6:case 7:L.slice_del()}}function k(){var e;if(L.ket=L.cursor,(e=L.find_among_b(P,12))&&(L.bra=L.cursor,i()))switch(e){case 1:case 4:case 7:case 9:L.slice_del();break;case 2:case 5:case 8:L.slice_from("e");break;case 3:case 6:L.slice_from("a")}}function f(){var e;if(L.ket=L.cursor,(e=L.find_among_b(F,31))&&(L.bra=L.cursor,i()))switch(e){case 1:case 4:case 7:case 8:case 9:case 12:case 13:case 16:case 17:case 18:L.slice_del();break;case 2:case 5:case 10:case 14:case 19:L.slice_from("a");break;case 3:case 6:case 11:case 15:case 20:L.slice_from("e")}}function b(){var e;if(L.ket=L.cursor,(e=L.find_among_b(S,42))&&(L.bra=L.cursor,i()))switch(e){case 1:case 4:case 5:case 6:case 9:case 10:case 11:case 14:case 15:case 16:case 17:case 20:case 21:case 24:case 25:case 26:case 29:L.slice_del();break;case 2:case 7:case 12:case 18:case 22:case 27:L.slice_from("a");break;case 3:case 8:case 13:case 19:case 23:case 28:L.slice_from("e")}}var d,g=[new n("cs",-1,-1),new n("dzs",-1,-1),new n("gy",-1,-1),new n("ly",-1,-1),new n("ny",-1,-1),new n("sz",-1,-1),new n("ty",-1,-1),new n("zs",-1,-1)],h=[new n("á",-1,1),new n("é",-1,2)],p=[new n("bb",-1,-1),new n("cc",-1,-1),new n("dd",-1,-1),new n("ff",-1,-1),new n("gg",-1,-1),new n("jj",-1,-1),new n("kk",-1,-1),new n("ll",-1,-1),new n("mm",-1,-1),new n("nn",-1,-1),new n("pp",-1,-1),new n("rr",-1,-1),new n("ccs",-1,-1),new n("ss",-1,-1),new n("zzs",-1,-1),new n("tt",-1,-1),new n("vv",-1,-1),new n("ggy",-1,-1),new n("lly",-1,-1),new n("nny",-1,-1),new n("tty",-1,-1),new n("ssz",-1,-1),new n("zz",-1,-1)],_=[new n("al",-1,1),new n("el",-1,2)],v=[new n("ba",-1,-1),new n("ra",-1,-1),new n("be",-1,-1),new n("re",-1,-1),new n("ig",-1,-1),new n("nak",-1,-1),new n("nek",-1,-1),new n("val",-1,-1),new n("vel",-1,-1),new n("ul",-1,-1),new n("nál",-1,-1),new n("nél",-1,-1),new n("ból",-1,-1),new n("ról",-1,-1),new n("tól",-1,-1),new n("bõl",-1,-1),new n("rõl",-1,-1),new n("tõl",-1,-1),new n("ül",-1,-1),new n("n",-1,-1),new n("an",19,-1),new n("ban",20,-1),new n("en",19,-1),new n("ben",22,-1),new n("képpen",22,-1),new n("on",19,-1),new n("ön",19,-1),new n("képp",-1,-1),new n("kor",-1,-1),new n("t",-1,-1),new n("at",29,-1),new n("et",29,-1),new n("ként",29,-1),new n("anként",32,-1),new n("enként",32,-1),new n("onként",32,-1),new n("ot",29,-1),new n("ért",29,-1),new n("öt",29,-1),new n("hez",-1,-1),new n("hoz",-1,-1),new n("höz",-1,-1),new n("vá",-1,-1),new n("vé",-1,-1)],z=[new n("án",-1,2),new n("én",-1,1),new n("ánként",-1,3)],y=[new n("stul",-1,2),new n("astul",0,1),new n("ástul",0,3),new n("stül",-1,2),new n("estül",3,1),new n("éstül",3,4)],j=[new n("á",-1,1),new n("é",-1,2)],C=[new n("k",-1,7),new n("ak",0,4),new n("ek",0,6),new n("ok",0,5),new n("ák",0,1),new n("ék",0,2),new n("ök",0,3)],P=[new n("éi",-1,7),new n("áéi",0,6),new n("ééi",0,5),new n("é",-1,9),new n("ké",3,4),new n("aké",4,1),new n("eké",4,1),new n("oké",4,1),new n("áké",4,3),new n("éké",4,2),new n("öké",4,1),new n("éé",3,8)],F=[new n("a",-1,18),new n("ja",0,17),new n("d",-1,16),new n("ad",2,13),new n("ed",2,13),new n("od",2,13),new n("ád",2,14),new n("éd",2,15),new n("öd",2,13),new n("e",-1,18),new n("je",9,17),new n("nk",-1,4),new n("unk",11,1),new n("ánk",11,2),new n("énk",11,3),new n("ünk",11,1),new n("uk",-1,8),new n("juk",16,7),new n("ájuk",17,5),new n("ük",-1,8),new n("jük",19,7),new n("éjük",20,6),new n("m",-1,12),new n("am",22,9),new n("em",22,9),new n("om",22,9),new n("ám",22,10),new n("ém",22,11),new n("o",-1,18),new n("á",-1,19),new n("é",-1,20)],S=[new n("id",-1,10),new n("aid",0,9),new n("jaid",1,6),new n("eid",0,9),new n("jeid",3,6),new n("áid",0,7),new n("éid",0,8),new n("i",-1,15),new n("ai",7,14),new n("jai",8,11),new n("ei",7,14),new n("jei",10,11),new n("ái",7,12),new n("éi",7,13),new n("itek",-1,24),new n("eitek",14,21),new n("jeitek",15,20),new n("éitek",14,23),new n("ik",-1,29),new n("aik",18,26),new n("jaik",19,25),new n("eik",18,26),new n("jeik",21,25),new n("áik",18,27),new n("éik",18,28),new n("ink",-1,20),new n("aink",25,17),new n("jaink",26,16),new n("eink",25,17),new n("jeink",28,16),new n("áink",25,18),new n("éink",25,19),new n("aitok",-1,21),new n("jaitok",32,20),new n("áitok",-1,22),new n("im",-1,5),new n("aim",35,4),new n("jaim",36,1),new n("eim",35,4),new n("jeim",38,1),new n("áim",35,2),new n("éim",35,3)],W=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,1,17,52,14],L=new r;this.setCurrent=function(e){L.setCurrent(e)},this.getCurrent=function(){return L.getCurrent()},this.stem=function(){var n=L.cursor;return e(),L.limit_backward=n,L.cursor=L.limit,c(),L.cursor=L.limit,o(),L.cursor=L.limit,w(),L.cursor=L.limit,l(),L.cursor=L.limit,u(),L.cursor=L.limit,k(),L.cursor=L.limit,f(),L.cursor=L.limit,b(),L.cursor=L.limit,m(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.hu.stemmer,"stemmer-hu"),e.hu.stopWordFilter=e.generateStopWordFilter("a abban ahhoz ahogy ahol aki akik akkor alatt amely amelyek amelyekben amelyeket amelyet amelynek ami amikor amit amolyan amíg annak arra arról az azok azon azonban azt aztán azután azzal azért be belül benne bár cikk cikkek cikkeket csak de e ebben eddig egy egyes egyetlen egyik egyre egyéb egész ehhez ekkor el ellen elsõ elég elõ elõször elõtt emilyen ennek erre ez ezek ezen ezt ezzel ezért fel felé hanem hiszen hogy hogyan igen ill ill. illetve ilyen ilyenkor ismét ison itt jobban jó jól kell kellett keressünk keresztül ki kívül között közül legalább legyen lehet lehetett lenne lenni lesz lett maga magát majd majd meg mellett mely melyek mert mi mikor milyen minden mindenki mindent mindig mint mintha mit mivel miért most már más másik még míg nagy nagyobb nagyon ne nekem neki nem nincs néha néhány nélkül olyan ott pedig persze rá s saját sem semmi sok sokat sokkal szemben szerint szinte számára talán tehát teljes tovább továbbá több ugyanis utolsó után utána vagy vagyis vagyok valaki valami valamint való van vannak vele vissza viszont volna volt voltak voltam voltunk által általában át én éppen és így õ õk õket össze úgy új újabb újra".split(" ")),e.Pipeline.registerFunction(e.hu.stopWordFilter,"stopWordFilter-hu")}}); -------------------------------------------------------------------------------- /docs/guide/metrics.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Prometheus metrics 3 | summary: The various metrics titan supports. 4 | authors: 5 | - John Coene 6 | --- 7 | 8 | # Metrics 9 | 10 | Prometheus provides four types of metrics. This document only briefly explains them, please refer to the [official documentation](https://prometheus.io/docs/concepts/metric_types/) if you need to learn more about it. 11 | 12 | Note that of the four types the most commonly used are the Counter and Gauge, the Histogram and Summary are extremely useful but used must less as they are most complicated to fully understand and set up correctly. Many client libraries of Prometheus do not even provide support for Histograms and Summaries. 13 | 14 | ## Basics 15 | 16 | Every metrics must bear a unique name and help text. Titan will not let you override a metric: make sure the names (identifiers) are unique. 17 | 18 | !!! Note 19 | Make sure metrics bear a unique name. 20 | 21 | The help text is also mandatory as per Prometheus, it allows giving more context on the metric tracked. 22 | 23 | Optionally, metrics can also take labels, which will be detailed later in this document. 24 | 25 | All metrics are R6 classes, you can have any number of these metrics in any project. 26 | 27 | ## Counter 28 | 29 | A counter is the most basic metrics one can use. It consists of a simple counter that can only go up; the value of counters can never decrease. 30 | 31 | !!! tip 32 | The value of counters can only increase, use the Gauge if the value may decrease. 33 | 34 | This can be used to measure the number of times an application is visited, or the number of times an endpoint is hit: these values only ever go up. Never use Counters for values that go down, titan will not let you decrease their value. 35 | 36 | Instantiate a new counter from the `Counter` R6 class, give it a name and some help text, then use the method `inc` to increase it. You can also use the method `set` to set it to a specific value, again make sure that value is greater than that which the counter already holds or it will throw a warning and _not set the counter to that value._ 37 | 38 | ```r 39 | c <- Counter$new( 40 | name = "btn_clicks_total", 41 | help = "Total clicks on buttons" 42 | ) 43 | 44 | c$inc() # increase 45 | c$inc(2) 46 | 47 | # preview the metrics 48 | previewMetrics() 49 | ``` 50 | 51 | ``` 52 | # HELP btn_clicks_total Total clicks on buttons 53 | # TYPE btn_clicks_total counter 54 | btn_clicks_total 3 55 | ``` 56 | 57 | ## Gauge 58 | 59 | A gauge is very similar to the Counter at the exception that its value can decrease. 60 | 61 | Then again this is set up in similar way as the Counter, the only difference is that it also has a `dec` method to decrease the gauge. 62 | 63 | ```r 64 | g <- Gauge$new( 65 | name = "current_users_total", 66 | help = "Total number of users right now" 67 | ) 68 | 69 | g$inc(2) # increase by 2 70 | g$dec() # decrease by 1 71 | 72 | # preview the metrics 73 | previewMetrics() 74 | ``` 75 | 76 | ``` 77 | # HELP current_users_total Total number of users right now 78 | # TYPE current_users_total gauge 79 | current_users_total 1 80 | ``` 81 | 82 | So why would you use a Counter when a Gauge does the same and more? Because this is stored and processed differently by Prometheus. Prometheus is, at its core, a time series database and will take the metric type into account when reporting metrics. 83 | 84 | !!! tip 85 | Gauges and counters are fundamentally stored as different data types; do not simply switch one for the other, think thoroughly about what you measure. 86 | 87 | ## Histogram 88 | 89 | Histograms allow you to count observations and put them in configurable buckets. 90 | 91 | Start by declaring a predicate; a function which will turn put the observations into buckets. A bucket is defined using the `bucket` function which takes 1) the label of the bucket, and 2) the value of said bucket. Below we create a predicate that will put the observations into two buckets. It will be used to measure the time it take to process a request, if the request takes more than 3 seconds it goes into a bucket called "3" and if it takes over 3 seconds it will put the observation into another bucket called "9". 92 | 93 | ```r 94 | # predicate to put observations into buckets. 95 | pred <- function(value){ 96 | v <- as.numeric(value) 97 | 98 | # put in bucket 3 if less than 3 seconds 99 | if(v < 3) 100 | return(bucket(label = "3", v)) 101 | 102 | # otherwise put in bucket 9 103 | bucket(label = "9", v) 104 | } 105 | ``` 106 | 107 | The creation of the histogram itself differs little from other metrics: specify a unique name, help text, and pass the predicate function previously defined. 108 | 109 | ```r 110 | h <- Histogram$new( 111 | name = "request_process_time", 112 | help = "Time it took to process the request", 113 | predicate = pred 114 | ) 115 | ``` 116 | 117 | Here, to demonstrate, we create a function to simulate a request taking time by randomly making the function sleep for between 1 and 9 seconds. When the function exits we `observe` the time difference between the beginning and end of the function. The `observe` method will internally run the `predicate` and place the results into buckets. 118 | 119 | ```r 120 | simulateRequest <- function(){ 121 | # time at which the request starts 122 | start <- Sys.time() 123 | 124 | # when done 125 | on.exit({ 126 | # compute time difference 127 | diff <- Sys.time() - start 128 | 129 | # observe data 130 | # will internally run `pred` 131 | h$observe(diff) 132 | }) 133 | 134 | # sleep between 1 and 9 seconds 135 | Sys.sleep(sample(1:9, 1)) 136 | 137 | print("done") 138 | } 139 | 140 | # simulate some requests 141 | simulateRequest() 142 | simulateRequest() 143 | simulateRequest() 144 | simulateRequest() 145 | 146 | # preview the metrics 147 | previewMetrics() 148 | ``` 149 | 150 | ``` 151 | # HELP request_process_time Time it took to process the request 152 | # TYPE request_process_time histogram 153 | request_process_time {le="3"} 2 154 | request_process_time {le="9"} 2 155 | request_process_time_count 4 156 | request_process_time_sum 13.0094513893127 157 | ``` 158 | 159 | Note that the histogram (as per Prometheus standards) also logs the `count`, the number of observations and the `sum` the sum of the observations. Above we can see that 4 requests were made that took a total of ~13 seconds; 2 of these took less than 3 seconds and the other 2 took more than that. 160 | 161 | ## Summary 162 | 163 | The Summary metric is very similar to the histogram, and works the same with Titan (predicate, etc.) except it does not count the observations in each buckets, instead it computes the sum of it. Also these buckets in Summary are called quantiles and must be between zero and one (0 < q < 1). 164 | 165 | ## Labels 166 | 167 | Labels allow adding granularity to metrics without duplicating them, they can be applied to any metric. 168 | 169 | From the [official documentation](https://prometheus.io/docs/practices/naming/#labels): 170 | 171 | !!! warning 172 | Remember that every unique combination of key-value label pairs represents a new time series, which can dramatically increase the amount of data stored. Do not use labels to store dimensions with high cardinality (many different label values), such as user IDs, email addresses, or other unbounded sets of values. --- Official documentation 173 | 174 | Say for instance you have a small API with three endpoints and simply want to track the number of times they get pinged. 175 | 176 | !!! Tip 177 | All labels specified must be used or titan will throw a warning and ignore the action. 178 | 179 | Though you could create three separate Counters it might be more convenient to create a simple Counter with a label that can be set to the path that is used. 180 | 181 | ```r 182 | c <- Counter$new( 183 | name = "api_visits_total", 184 | help = "Total API visits", 185 | labels = "endpoint" 186 | ) 187 | 188 | c$inc(1, endpoint = "/") 189 | c$inc(1, endpoint = "/count") 190 | c$inc(1, endpoint = "/home") 191 | c$inc(2, endpoint = "/home") 192 | 193 | previewMetrics() 194 | ``` 195 | 196 | ``` 197 | # HELP api_visits_total Total API visits 198 | # TYPE api_visits_total counter 199 | api_visits_total {endpoint="/"} 1 200 | api_visits_total {endpoint="/count"} 1 201 | api_visits_total {endpoint="/home"} 3 202 | ``` 203 | 204 | If you use `labels` you must specify all of the labels every time you change the value of the metric (`inc`, `dec`, `set`, `observe`). Otherwise titan throws a warning and ignores the action. 205 | 206 | !!! error 207 | Since all labels must be specified this will fail. 208 | 209 | ```r hl_lines="21" 210 | c <- Counter$new( 211 | "btn_clicks_total", 212 | "Total button clicks", 213 | labels = c("color", "module") 214 | ) 215 | 216 | # fails 217 | # missing all labels 218 | c$inc() 219 | 220 | # fails 221 | # missing one label 222 | c$inc(color = "blue") 223 | 224 | # fails 225 | # missing another label 226 | c$inc(module = "homepage") 227 | 228 | # Succeeds 229 | # both labels present 230 | c$inc(color = "blue", module = "homepage") 231 | ``` 232 | -------------------------------------------------------------------------------- /R/metrics.R: -------------------------------------------------------------------------------- 1 | #' Metrics 2 | #' 3 | #' @noRd 4 | #' @keywords internal 5 | Metric <- R6::R6Class( 6 | "Metric", 7 | public = list( 8 | #' @details Initialise 9 | #' 10 | #' @param name Name of the metric. 11 | #' @param help Help text describing the metric. 12 | #' @param labels Character vector of labels available. 13 | #' @param unit Unit of metric. 14 | #' @param type Metric type. 15 | #' @param renderMeta Whether to render the metadata. 16 | initialize = function(name, help, labels = NULL, unit = NULL, 17 | type = c("gauge", "counter", "histogram", "summary"), 18 | renderMeta = TRUE){ 19 | 20 | if(missing(name)) 21 | stop("Missing `name`", call. = FALSE) 22 | 23 | if(missing(help)) 24 | stop("Missing `help`", call. = FALSE) 25 | 26 | type <- match.arg(type) 27 | ns <- getTitanNamespace() 28 | 29 | if(!is.null(ns)) 30 | name <- sprintf("%s_%s", ns, name) 31 | 32 | # store name we need it later 33 | private$.name <- name 34 | private$.renderMeta <- renderMeta 35 | 36 | # save for retrieval checks 37 | private$.metricType <- type 38 | 39 | # render those, no need to re-render at every ping 40 | private$.help <- renderHelp(name, help) 41 | private$.unit <- renderUnit(name, unit) 42 | private$.type <- renderType(name, type) 43 | 44 | # allow NULL 45 | if(is.null(labels)) 46 | labels <- c() 47 | 48 | # force character 49 | labels <- as.character(labels) 50 | # order for upsert to work correctly 51 | private$.labels <- orderLabels(labels) 52 | }, 53 | #' @details Set the metric to a current value given labels. 54 | #' @param val Value to set the metric. 55 | #' @param ... Key value pairs of labels. 56 | set = function(val, ...){ 57 | name <- private$.makeLabelName(...) 58 | 59 | if(is.error(name)) 60 | return(invisible(name)) 61 | 62 | private$.values[[name]] <- val 63 | 64 | invisible(self) 65 | }, 66 | #' @details Retrieve the value of a metric given labels 67 | #' @param ... Key value pairs of labels. 68 | get = function(...){ 69 | name <- private$.makeLabelName(...) 70 | 71 | if(is.error(name)) 72 | return(invisible(name)) 73 | 74 | private$.values[[name]] %||%0 75 | }, 76 | #' @details Increase the metric to a current value given labels. 77 | #' @param val Value to increase the metric. 78 | #' @param ... Key value pairs of labels. 79 | inc = function(val = 1, ...){ 80 | current <- self$get(...) 81 | if(is.error(current)) 82 | return(invisible(current)) 83 | newValue <- current + val 84 | self$set(newValue, ...) 85 | 86 | invisible(self) 87 | }, 88 | #' @details Decrease the metric to a current value given labels. 89 | #' @param val Value to decrease the metric. 90 | #' @param ... Key value pairs of labels. 91 | dec = function(val = 1, ...){ 92 | current <- self$get(...) 93 | if(is.error(current)) 94 | return(invisible(current)) 95 | newValue <- current - val 96 | self$set(newValue, ...) 97 | 98 | invisible(self) 99 | }, 100 | #' @details Retrieve the metric type 101 | getType = function(){ 102 | private$.metricType 103 | }, 104 | #' @details Render the metric 105 | render = function(){ 106 | 107 | values <- private$.values 108 | 109 | # no value return nothing 110 | if(length(values) == 0) 111 | return("") 112 | 113 | # remove UNIQUE name 114 | if(names(values)[1] == "UNIQUE") 115 | names(values) <- "" 116 | 117 | values <- paste0( 118 | private$.name, 119 | names(values)," ", 120 | values 121 | ) 122 | 123 | values <- paste0(values, collapse = "\n") 124 | 125 | # render meta 126 | if(private$.renderMeta){ 127 | meta <- sprintf( 128 | "%s%s%s", 129 | private$.help, 130 | private$.type, 131 | private$.unit 132 | ) 133 | 134 | values <- paste0(meta, values) 135 | } 136 | 137 | paste0(values, sep = "\n", collapse = "") 138 | } 139 | ), 140 | private = list( 141 | .registry = NULL, 142 | .values = list(), 143 | .name = "", 144 | .help = "", 145 | .labels = c(), 146 | .unit = "", 147 | .type = "", 148 | .metricType = "", 149 | .renderMeta = TRUE, 150 | .makeLabelName = function(...){ 151 | labels <- c(...) 152 | 153 | ok <- private$.validateLabels(labels) 154 | 155 | if(is.error(ok)) 156 | return(ok) 157 | 158 | # no labels, using UNIQUE as name 159 | if(length(labels) == 0) 160 | return("UNIQUE") 161 | 162 | labels <- orderLabels(labels) 163 | 164 | values <- paste0( 165 | names(labels), '="', unname(labels), '"', 166 | collapse = "," 167 | ) 168 | 169 | paste0("{", values, "}") 170 | }, 171 | .validateLabels = function(labels){ 172 | if(length(private$.labels) != length(labels)){ 173 | warnLabels(labels, private$.labels) 174 | return(Error()) 175 | } 176 | 177 | if(length(private$.labels) == 0 && length(labels) == 0) 178 | return(TRUE) 179 | 180 | if(length(labels) != length(private$.labels)){ 181 | warnLabels(labels, private$.labels) 182 | return(Error()) 183 | } 184 | 185 | match <- all(names(labels) %in% private$.labels) 186 | 187 | if(!match){ 188 | warnLabels(labels, private$.labels) 189 | return(Error()) 190 | } 191 | 192 | return(TRUE) 193 | } 194 | ) 195 | ) 196 | 197 | #' Metrics Interface 198 | #' 199 | #' Exposed interface to the metrics. 200 | #' 201 | #' @export 202 | MetricInterface <- R6::R6Class( 203 | "MetricInterface", 204 | public = list( 205 | #' @details Initialise 206 | #' 207 | #' @param name Name of the metric. 208 | #' @param help Help text describing the metric. 209 | #' @param labels Character vector of labels available. 210 | #' @param unit Unit of metric. 211 | #' @param type Metric type. 212 | #' @param renderMeta Whether to render the metadata. 213 | initialize = function(name, help, labels = NULL, unit = NULL, 214 | type = c("gauge", "counter", "histogram", "summary"), 215 | renderMeta = TRUE){ 216 | 217 | private$.name <- name 218 | 219 | # don't override! 220 | existing <- registryGet(name) 221 | if(!is.null(existing)){ 222 | existingType <- existing$getType() 223 | 224 | if(existingType != type) 225 | stop("Metric name already used", call. = FALSE) 226 | 227 | return(self) 228 | } 229 | 230 | registrySet( 231 | name, 232 | Metric$new( 233 | name, help, labels = labels, unit = unit, 234 | type = type, renderMeta = renderMeta 235 | ) 236 | ) 237 | }, 238 | #' @details Set the metric to a current value given labels. 239 | #' @param val Value to set the metric. 240 | #' @param ... Key value pairs of labels. 241 | set = function(val, ...){ 242 | registryGet(private$.name)$set(val, ...) 243 | }, 244 | #' @details Retrieve the value of a metric given labels 245 | #' @param ... Key value pairs of labels. 246 | get = function(...){ 247 | registryGet(private$.name)$get(...) 248 | }, 249 | #' @details Increase the metric to a current value given labels. 250 | #' @param val Value to increase the metric. 251 | #' @param ... Key value pairs of labels. 252 | inc = function(val = 1, ...){ 253 | registryGet(private$.name)$inc(val, ...) 254 | }, 255 | #' @details Decrease the metric to a current value given labels. 256 | #' @param val Value to decrease the metric. 257 | #' @param ... Key value pairs of labels. 258 | dec = function(val = 1, ...){ 259 | registryGet(private$.name)$dec(val, ...) 260 | }, 261 | #' @details Render the metric 262 | render = function(){ 263 | registryGet(private$.name)$render() 264 | } 265 | ), 266 | private = list( 267 | .name = "" 268 | ) 269 | ) 270 | 271 | #' Order labels 272 | #' 273 | #' Orders the labels to ensure they are always stored 274 | #' in the same order regardless of the order in which 275 | #' they are passed to the various functions. This ensures 276 | #' the "upsert" mechanism works. 277 | #' 278 | #' @param labels Character vector of labels. 279 | #' 280 | #' @noRd 281 | #' @keywords internal 282 | orderLabels <- function(labels){ 283 | labels[order(labels)] 284 | } 285 | 286 | #' Warning on wrong labels 287 | #' 288 | #' @param received Labels received. 289 | #' @param expected Labels expected. 290 | #' 291 | #' @noRd 292 | #' @keywords internal 293 | warnLabels <- function(received, expected){ 294 | warning( 295 | "[IGNORING] labels mismatch.\nReceived: ", 296 | collapseLabels(names(received)), 297 | "\nExpected: ", 298 | collapseLabels(expected), 299 | call. = FALSE 300 | ) 301 | } 302 | 303 | #' @noRd 304 | #' @keywords internal 305 | collapseLabels <- function(labels){ 306 | if(length(labels) == 0) 307 | return("") 308 | paste0("`", labels, "`", collapse = ", ") 309 | } -------------------------------------------------------------------------------- /site/assets/javascripts/lunr/min/lunr.pt.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Lunr languages, `Portuguese` language 3 | * https://github.com/MihaiValentin/lunr-languages 4 | * 5 | * Copyright 2014, Mihai Valentin 6 | * http://www.mozilla.org/MPL/ 7 | */ 8 | /*! 9 | * based on 10 | * Snowball JavaScript Library v0.3 11 | * http://code.google.com/p/urim/ 12 | * http://snowball.tartarus.org/ 13 | * 14 | * Copyright 2010, Oleg Mazko 15 | * http://www.mozilla.org/MPL/ 16 | */ 17 | 18 | !function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.pt=function(){this.pipeline.reset(),this.pipeline.add(e.pt.trimmer,e.pt.stopWordFilter,e.pt.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.pt.stemmer))},e.pt.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.pt.trimmer=e.trimmerSupport.generateTrimmer(e.pt.wordCharacters),e.Pipeline.registerFunction(e.pt.trimmer,"trimmer-pt"),e.pt.stemmer=function(){var r=e.stemmerSupport.Among,s=e.stemmerSupport.SnowballProgram,n=new function(){function e(){for(var e;;){if(z.bra=z.cursor,e=z.find_among(k,3))switch(z.ket=z.cursor,e){case 1:z.slice_from("a~");continue;case 2:z.slice_from("o~");continue;case 3:if(z.cursor>=z.limit)break;z.cursor++;continue}break}}function n(){if(z.out_grouping(y,97,250)){for(;!z.in_grouping(y,97,250);){if(z.cursor>=z.limit)return!0;z.cursor++}return!1}return!0}function i(){if(z.in_grouping(y,97,250))for(;!z.out_grouping(y,97,250);){if(z.cursor>=z.limit)return!1;z.cursor++}return g=z.cursor,!0}function o(){var e,r,s=z.cursor;if(z.in_grouping(y,97,250))if(e=z.cursor,n()){if(z.cursor=e,i())return}else g=z.cursor;if(z.cursor=s,z.out_grouping(y,97,250)){if(r=z.cursor,n()){if(z.cursor=r,!z.in_grouping(y,97,250)||z.cursor>=z.limit)return;z.cursor++}g=z.cursor}}function t(){for(;!z.in_grouping(y,97,250);){if(z.cursor>=z.limit)return!1;z.cursor++}for(;!z.out_grouping(y,97,250);){if(z.cursor>=z.limit)return!1;z.cursor++}return!0}function a(){var e=z.cursor;g=z.limit,b=g,h=g,o(),z.cursor=e,t()&&(b=z.cursor,t()&&(h=z.cursor))}function u(){for(var e;;){if(z.bra=z.cursor,e=z.find_among(q,3))switch(z.ket=z.cursor,e){case 1:z.slice_from("ã");continue;case 2:z.slice_from("õ");continue;case 3:if(z.cursor>=z.limit)break;z.cursor++;continue}break}}function w(){return g<=z.cursor}function m(){return b<=z.cursor}function c(){return h<=z.cursor}function l(){var e;if(z.ket=z.cursor,!(e=z.find_among_b(F,45)))return!1;switch(z.bra=z.cursor,e){case 1:if(!c())return!1;z.slice_del();break;case 2:if(!c())return!1;z.slice_from("log");break;case 3:if(!c())return!1;z.slice_from("u");break;case 4:if(!c())return!1;z.slice_from("ente");break;case 5:if(!m())return!1;z.slice_del(),z.ket=z.cursor,e=z.find_among_b(j,4),e&&(z.bra=z.cursor,c()&&(z.slice_del(),1==e&&(z.ket=z.cursor,z.eq_s_b(2,"at")&&(z.bra=z.cursor,c()&&z.slice_del()))));break;case 6:if(!c())return!1;z.slice_del(),z.ket=z.cursor,e=z.find_among_b(C,3),e&&(z.bra=z.cursor,1==e&&c()&&z.slice_del());break;case 7:if(!c())return!1;z.slice_del(),z.ket=z.cursor,e=z.find_among_b(P,3),e&&(z.bra=z.cursor,1==e&&c()&&z.slice_del());break;case 8:if(!c())return!1;z.slice_del(),z.ket=z.cursor,z.eq_s_b(2,"at")&&(z.bra=z.cursor,c()&&z.slice_del());break;case 9:if(!w()||!z.eq_s_b(1,"e"))return!1;z.slice_from("ir")}return!0}function f(){var e,r;if(z.cursor>=g){if(r=z.limit_backward,z.limit_backward=g,z.ket=z.cursor,e=z.find_among_b(S,120))return z.bra=z.cursor,1==e&&z.slice_del(),z.limit_backward=r,!0;z.limit_backward=r}return!1}function d(){var e;z.ket=z.cursor,(e=z.find_among_b(W,7))&&(z.bra=z.cursor,1==e&&w()&&z.slice_del())}function v(e,r){if(z.eq_s_b(1,e)){z.bra=z.cursor;var s=z.limit-z.cursor;if(z.eq_s_b(1,r))return z.cursor=z.limit-s,w()&&z.slice_del(),!1}return!0}function p(){var e;if(z.ket=z.cursor,e=z.find_among_b(L,4))switch(z.bra=z.cursor,e){case 1:w()&&(z.slice_del(),z.ket=z.cursor,z.limit-z.cursor,v("u","g")&&v("i","c"));break;case 2:z.slice_from("c")}}function _(){if(!l()&&(z.cursor=z.limit,!f()))return z.cursor=z.limit,void d();z.cursor=z.limit,z.ket=z.cursor,z.eq_s_b(1,"i")&&(z.bra=z.cursor,z.eq_s_b(1,"c")&&(z.cursor=z.limit,w()&&z.slice_del()))}var h,b,g,k=[new r("",-1,3),new r("ã",0,1),new r("õ",0,2)],q=[new r("",-1,3),new r("a~",0,1),new r("o~",0,2)],j=[new r("ic",-1,-1),new r("ad",-1,-1),new r("os",-1,-1),new r("iv",-1,1)],C=[new r("ante",-1,1),new r("avel",-1,1),new r("ível",-1,1)],P=[new r("ic",-1,1),new r("abil",-1,1),new r("iv",-1,1)],F=[new r("ica",-1,1),new r("ância",-1,1),new r("ência",-1,4),new r("ira",-1,9),new r("adora",-1,1),new r("osa",-1,1),new r("ista",-1,1),new r("iva",-1,8),new r("eza",-1,1),new r("logía",-1,2),new r("idade",-1,7),new r("ante",-1,1),new r("mente",-1,6),new r("amente",12,5),new r("ável",-1,1),new r("ível",-1,1),new r("ución",-1,3),new r("ico",-1,1),new r("ismo",-1,1),new r("oso",-1,1),new r("amento",-1,1),new r("imento",-1,1),new r("ivo",-1,8),new r("aça~o",-1,1),new r("ador",-1,1),new r("icas",-1,1),new r("ências",-1,4),new r("iras",-1,9),new r("adoras",-1,1),new r("osas",-1,1),new r("istas",-1,1),new r("ivas",-1,8),new r("ezas",-1,1),new r("logías",-1,2),new r("idades",-1,7),new r("uciones",-1,3),new r("adores",-1,1),new r("antes",-1,1),new r("aço~es",-1,1),new r("icos",-1,1),new r("ismos",-1,1),new r("osos",-1,1),new r("amentos",-1,1),new r("imentos",-1,1),new r("ivos",-1,8)],S=[new r("ada",-1,1),new r("ida",-1,1),new r("ia",-1,1),new r("aria",2,1),new r("eria",2,1),new r("iria",2,1),new r("ara",-1,1),new r("era",-1,1),new r("ira",-1,1),new r("ava",-1,1),new r("asse",-1,1),new r("esse",-1,1),new r("isse",-1,1),new r("aste",-1,1),new r("este",-1,1),new r("iste",-1,1),new r("ei",-1,1),new r("arei",16,1),new r("erei",16,1),new r("irei",16,1),new r("am",-1,1),new r("iam",20,1),new r("ariam",21,1),new r("eriam",21,1),new r("iriam",21,1),new r("aram",20,1),new r("eram",20,1),new r("iram",20,1),new r("avam",20,1),new r("em",-1,1),new r("arem",29,1),new r("erem",29,1),new r("irem",29,1),new r("assem",29,1),new r("essem",29,1),new r("issem",29,1),new r("ado",-1,1),new r("ido",-1,1),new r("ando",-1,1),new r("endo",-1,1),new r("indo",-1,1),new r("ara~o",-1,1),new r("era~o",-1,1),new r("ira~o",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("ir",-1,1),new r("as",-1,1),new r("adas",47,1),new r("idas",47,1),new r("ias",47,1),new r("arias",50,1),new r("erias",50,1),new r("irias",50,1),new r("aras",47,1),new r("eras",47,1),new r("iras",47,1),new r("avas",47,1),new r("es",-1,1),new r("ardes",58,1),new r("erdes",58,1),new r("irdes",58,1),new r("ares",58,1),new r("eres",58,1),new r("ires",58,1),new r("asses",58,1),new r("esses",58,1),new r("isses",58,1),new r("astes",58,1),new r("estes",58,1),new r("istes",58,1),new r("is",-1,1),new r("ais",71,1),new r("eis",71,1),new r("areis",73,1),new r("ereis",73,1),new r("ireis",73,1),new r("áreis",73,1),new r("éreis",73,1),new r("íreis",73,1),new r("ásseis",73,1),new r("ésseis",73,1),new r("ísseis",73,1),new r("áveis",73,1),new r("íeis",73,1),new r("aríeis",84,1),new r("eríeis",84,1),new r("iríeis",84,1),new r("ados",-1,1),new r("idos",-1,1),new r("amos",-1,1),new r("áramos",90,1),new r("éramos",90,1),new r("íramos",90,1),new r("ávamos",90,1),new r("íamos",90,1),new r("aríamos",95,1),new r("eríamos",95,1),new r("iríamos",95,1),new r("emos",-1,1),new r("aremos",99,1),new r("eremos",99,1),new r("iremos",99,1),new r("ássemos",99,1),new r("êssemos",99,1),new r("íssemos",99,1),new r("imos",-1,1),new r("armos",-1,1),new r("ermos",-1,1),new r("irmos",-1,1),new r("ámos",-1,1),new r("arás",-1,1),new r("erás",-1,1),new r("irás",-1,1),new r("eu",-1,1),new r("iu",-1,1),new r("ou",-1,1),new r("ará",-1,1),new r("erá",-1,1),new r("irá",-1,1)],W=[new r("a",-1,1),new r("i",-1,1),new r("o",-1,1),new r("os",-1,1),new r("á",-1,1),new r("í",-1,1),new r("ó",-1,1)],L=[new r("e",-1,1),new r("ç",-1,2),new r("é",-1,1),new r("ê",-1,1)],y=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,3,19,12,2],z=new s;this.setCurrent=function(e){z.setCurrent(e)},this.getCurrent=function(){return z.getCurrent()},this.stem=function(){var r=z.cursor;return e(),z.cursor=r,a(),z.limit_backward=r,z.cursor=z.limit,_(),z.cursor=z.limit,p(),z.cursor=z.limit_backward,u(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.pt.stemmer,"stemmer-pt"),e.pt.stopWordFilter=e.generateStopWordFilter("a ao aos aquela aquelas aquele aqueles aquilo as até com como da das de dela delas dele deles depois do dos e ela elas ele eles em entre era eram essa essas esse esses esta estamos estas estava estavam este esteja estejam estejamos estes esteve estive estivemos estiver estivera estiveram estiverem estivermos estivesse estivessem estivéramos estivéssemos estou está estávamos estão eu foi fomos for fora foram forem formos fosse fossem fui fôramos fôssemos haja hajam hajamos havemos hei houve houvemos houver houvera houveram houverei houverem houveremos houveria houveriam houvermos houverá houverão houveríamos houvesse houvessem houvéramos houvéssemos há hão isso isto já lhe lhes mais mas me mesmo meu meus minha minhas muito na nas nem no nos nossa nossas nosso nossos num numa não nós o os ou para pela pelas pelo pelos por qual quando que quem se seja sejam sejamos sem serei seremos seria seriam será serão seríamos seu seus somos sou sua suas são só também te tem temos tenha tenham tenhamos tenho terei teremos teria teriam terá terão teríamos teu teus teve tinha tinham tive tivemos tiver tivera tiveram tiverem tivermos tivesse tivessem tivéramos tivéssemos tu tua tuas tém tínhamos um uma você vocês vos à às éramos".split(" ")),e.Pipeline.registerFunction(e.pt.stopWordFilter,"stopWordFilter-pt")}}); -------------------------------------------------------------------------------- /site/assets/javascripts/lunr/min/lunr.fr.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Lunr languages, `French` language 3 | * https://github.com/MihaiValentin/lunr-languages 4 | * 5 | * Copyright 2014, Mihai Valentin 6 | * http://www.mozilla.org/MPL/ 7 | */ 8 | /*! 9 | * based on 10 | * Snowball JavaScript Library v0.3 11 | * http://code.google.com/p/urim/ 12 | * http://snowball.tartarus.org/ 13 | * 14 | * Copyright 2010, Oleg Mazko 15 | * http://www.mozilla.org/MPL/ 16 | */ 17 | 18 | !function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.fr=function(){this.pipeline.reset(),this.pipeline.add(e.fr.trimmer,e.fr.stopWordFilter,e.fr.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.fr.stemmer))},e.fr.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.fr.trimmer=e.trimmerSupport.generateTrimmer(e.fr.wordCharacters),e.Pipeline.registerFunction(e.fr.trimmer,"trimmer-fr"),e.fr.stemmer=function(){var r=e.stemmerSupport.Among,s=e.stemmerSupport.SnowballProgram,i=new function(){function e(e,r,s){return!(!W.eq_s(1,e)||(W.ket=W.cursor,!W.in_grouping(F,97,251)))&&(W.slice_from(r),W.cursor=s,!0)}function i(e,r,s){return!!W.eq_s(1,e)&&(W.ket=W.cursor,W.slice_from(r),W.cursor=s,!0)}function n(){for(var r,s;;){if(r=W.cursor,W.in_grouping(F,97,251)){if(W.bra=W.cursor,s=W.cursor,e("u","U",r))continue;if(W.cursor=s,e("i","I",r))continue;if(W.cursor=s,i("y","Y",r))continue}if(W.cursor=r,W.bra=r,!e("y","Y",r)){if(W.cursor=r,W.eq_s(1,"q")&&(W.bra=W.cursor,i("u","U",r)))continue;if(W.cursor=r,r>=W.limit)return;W.cursor++}}}function t(){for(;!W.in_grouping(F,97,251);){if(W.cursor>=W.limit)return!0;W.cursor++}for(;!W.out_grouping(F,97,251);){if(W.cursor>=W.limit)return!0;W.cursor++}return!1}function u(){var e=W.cursor;if(q=W.limit,g=q,p=q,W.in_grouping(F,97,251)&&W.in_grouping(F,97,251)&&W.cursor=W.limit){W.cursor=q;break}W.cursor++}while(!W.in_grouping(F,97,251))}q=W.cursor,W.cursor=e,t()||(g=W.cursor,t()||(p=W.cursor))}function o(){for(var e,r;;){if(r=W.cursor,W.bra=r,!(e=W.find_among(h,4)))break;switch(W.ket=W.cursor,e){case 1:W.slice_from("i");break;case 2:W.slice_from("u");break;case 3:W.slice_from("y");break;case 4:if(W.cursor>=W.limit)return;W.cursor++}}}function c(){return q<=W.cursor}function a(){return g<=W.cursor}function l(){return p<=W.cursor}function w(){var e,r;if(W.ket=W.cursor,e=W.find_among_b(C,43)){switch(W.bra=W.cursor,e){case 1:if(!l())return!1;W.slice_del();break;case 2:if(!l())return!1;W.slice_del(),W.ket=W.cursor,W.eq_s_b(2,"ic")&&(W.bra=W.cursor,l()?W.slice_del():W.slice_from("iqU"));break;case 3:if(!l())return!1;W.slice_from("log");break;case 4:if(!l())return!1;W.slice_from("u");break;case 5:if(!l())return!1;W.slice_from("ent");break;case 6:if(!c())return!1;if(W.slice_del(),W.ket=W.cursor,e=W.find_among_b(z,6))switch(W.bra=W.cursor,e){case 1:l()&&(W.slice_del(),W.ket=W.cursor,W.eq_s_b(2,"at")&&(W.bra=W.cursor,l()&&W.slice_del()));break;case 2:l()?W.slice_del():a()&&W.slice_from("eux");break;case 3:l()&&W.slice_del();break;case 4:c()&&W.slice_from("i")}break;case 7:if(!l())return!1;if(W.slice_del(),W.ket=W.cursor,e=W.find_among_b(y,3))switch(W.bra=W.cursor,e){case 1:l()?W.slice_del():W.slice_from("abl");break;case 2:l()?W.slice_del():W.slice_from("iqU");break;case 3:l()&&W.slice_del()}break;case 8:if(!l())return!1;if(W.slice_del(),W.ket=W.cursor,W.eq_s_b(2,"at")&&(W.bra=W.cursor,l()&&(W.slice_del(),W.ket=W.cursor,W.eq_s_b(2,"ic")))){W.bra=W.cursor,l()?W.slice_del():W.slice_from("iqU");break}break;case 9:W.slice_from("eau");break;case 10:if(!a())return!1;W.slice_from("al");break;case 11:if(l())W.slice_del();else{if(!a())return!1;W.slice_from("eux")}break;case 12:if(!a()||!W.out_grouping_b(F,97,251))return!1;W.slice_del();break;case 13:return c()&&W.slice_from("ant"),!1;case 14:return c()&&W.slice_from("ent"),!1;case 15:return r=W.limit-W.cursor,W.in_grouping_b(F,97,251)&&c()&&(W.cursor=W.limit-r,W.slice_del()),!1}return!0}return!1}function f(){var e,r;if(W.cursor=q){if(s=W.limit_backward,W.limit_backward=q,W.ket=W.cursor,e=W.find_among_b(P,7))switch(W.bra=W.cursor,e){case 1:if(l()){if(i=W.limit-W.cursor,!W.eq_s_b(1,"s")&&(W.cursor=W.limit-i,!W.eq_s_b(1,"t")))break;W.slice_del()}break;case 2:W.slice_from("i");break;case 3:W.slice_del();break;case 4:W.eq_s_b(2,"gu")&&W.slice_del()}W.limit_backward=s}}function b(){var e=W.limit-W.cursor;W.find_among_b(U,5)&&(W.cursor=W.limit-e,W.ket=W.cursor,W.cursor>W.limit_backward&&(W.cursor--,W.bra=W.cursor,W.slice_del()))}function d(){for(var e,r=1;W.out_grouping_b(F,97,251);)r--;if(r<=0){if(W.ket=W.cursor,e=W.limit-W.cursor,!W.eq_s_b(1,"é")&&(W.cursor=W.limit-e,!W.eq_s_b(1,"è")))return;W.bra=W.cursor,W.slice_from("e")}}function k(){if(!w()&&(W.cursor=W.limit,!f()&&(W.cursor=W.limit,!m())))return W.cursor=W.limit,void _();W.cursor=W.limit,W.ket=W.cursor,W.eq_s_b(1,"Y")?(W.bra=W.cursor,W.slice_from("i")):(W.cursor=W.limit,W.eq_s_b(1,"ç")&&(W.bra=W.cursor,W.slice_from("c")))}var p,g,q,v=[new r("col",-1,-1),new r("par",-1,-1),new r("tap",-1,-1)],h=[new r("",-1,4),new r("I",0,1),new r("U",0,2),new r("Y",0,3)],z=[new r("iqU",-1,3),new r("abl",-1,3),new r("Ièr",-1,4),new r("ièr",-1,4),new r("eus",-1,2),new r("iv",-1,1)],y=[new r("ic",-1,2),new r("abil",-1,1),new r("iv",-1,3)],C=[new r("iqUe",-1,1),new r("atrice",-1,2),new r("ance",-1,1),new r("ence",-1,5),new r("logie",-1,3),new r("able",-1,1),new r("isme",-1,1),new r("euse",-1,11),new r("iste",-1,1),new r("ive",-1,8),new r("if",-1,8),new r("usion",-1,4),new r("ation",-1,2),new r("ution",-1,4),new r("ateur",-1,2),new r("iqUes",-1,1),new r("atrices",-1,2),new r("ances",-1,1),new r("ences",-1,5),new r("logies",-1,3),new r("ables",-1,1),new r("ismes",-1,1),new r("euses",-1,11),new r("istes",-1,1),new r("ives",-1,8),new r("ifs",-1,8),new r("usions",-1,4),new r("ations",-1,2),new r("utions",-1,4),new r("ateurs",-1,2),new r("ments",-1,15),new r("ements",30,6),new r("issements",31,12),new r("ités",-1,7),new r("ment",-1,15),new r("ement",34,6),new r("issement",35,12),new r("amment",34,13),new r("emment",34,14),new r("aux",-1,10),new r("eaux",39,9),new r("eux",-1,1),new r("ité",-1,7)],x=[new r("ira",-1,1),new r("ie",-1,1),new r("isse",-1,1),new r("issante",-1,1),new r("i",-1,1),new r("irai",4,1),new r("ir",-1,1),new r("iras",-1,1),new r("ies",-1,1),new r("îmes",-1,1),new r("isses",-1,1),new r("issantes",-1,1),new r("îtes",-1,1),new r("is",-1,1),new r("irais",13,1),new r("issais",13,1),new r("irions",-1,1),new r("issions",-1,1),new r("irons",-1,1),new r("issons",-1,1),new r("issants",-1,1),new r("it",-1,1),new r("irait",21,1),new r("issait",21,1),new r("issant",-1,1),new r("iraIent",-1,1),new r("issaIent",-1,1),new r("irent",-1,1),new r("issent",-1,1),new r("iront",-1,1),new r("ît",-1,1),new r("iriez",-1,1),new r("issiez",-1,1),new r("irez",-1,1),new r("issez",-1,1)],I=[new r("a",-1,3),new r("era",0,2),new r("asse",-1,3),new r("ante",-1,3),new r("ée",-1,2),new r("ai",-1,3),new r("erai",5,2),new r("er",-1,2),new r("as",-1,3),new r("eras",8,2),new r("âmes",-1,3),new r("asses",-1,3),new r("antes",-1,3),new r("âtes",-1,3),new r("ées",-1,2),new r("ais",-1,3),new r("erais",15,2),new r("ions",-1,1),new r("erions",17,2),new r("assions",17,3),new r("erons",-1,2),new r("ants",-1,3),new r("és",-1,2),new r("ait",-1,3),new r("erait",23,2),new r("ant",-1,3),new r("aIent",-1,3),new r("eraIent",26,2),new r("èrent",-1,2),new r("assent",-1,3),new r("eront",-1,2),new r("ât",-1,3),new r("ez",-1,2),new r("iez",32,2),new r("eriez",33,2),new r("assiez",33,3),new r("erez",32,2),new r("é",-1,2)],P=[new r("e",-1,3),new r("Ière",0,2),new r("ière",0,2),new r("ion",-1,1),new r("Ier",-1,2),new r("ier",-1,2),new r("ë",-1,4)],U=[new r("ell",-1,-1),new r("eill",-1,-1),new r("enn",-1,-1),new r("onn",-1,-1),new r("ett",-1,-1)],F=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,128,130,103,8,5],S=[1,65,20,0,0,0,0,0,0,0,0,0,0,0,0,0,128],W=new s;this.setCurrent=function(e){W.setCurrent(e)},this.getCurrent=function(){return W.getCurrent()},this.stem=function(){var e=W.cursor;return n(),W.cursor=e,u(),W.limit_backward=e,W.cursor=W.limit,k(),W.cursor=W.limit,b(),W.cursor=W.limit,d(),W.cursor=W.limit_backward,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.fr.stemmer,"stemmer-fr"),e.fr.stopWordFilter=e.generateStopWordFilter("ai aie aient aies ait as au aura aurai auraient aurais aurait auras aurez auriez aurions aurons auront aux avaient avais avait avec avez aviez avions avons ayant ayez ayons c ce ceci celà ces cet cette d dans de des du elle en es est et eu eue eues eurent eus eusse eussent eusses eussiez eussions eut eux eûmes eût eûtes furent fus fusse fussent fusses fussiez fussions fut fûmes fût fûtes ici il ils j je l la le les leur leurs lui m ma mais me mes moi mon même n ne nos notre nous on ont ou par pas pour qu que quel quelle quelles quels qui s sa sans se sera serai seraient serais serait seras serez seriez serions serons seront ses soi soient sois soit sommes son sont soyez soyons suis sur t ta te tes toi ton tu un une vos votre vous y à étaient étais était étant étiez étions été étée étées étés êtes".split(" ")),e.Pipeline.registerFunction(e.fr.stopWordFilter,"stopWordFilter-fr")}}); -------------------------------------------------------------------------------- /site/assets/javascripts/lunr/min/lunr.ro.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Lunr languages, `Romanian` language 3 | * https://github.com/MihaiValentin/lunr-languages 4 | * 5 | * Copyright 2014, Mihai Valentin 6 | * http://www.mozilla.org/MPL/ 7 | */ 8 | /*! 9 | * based on 10 | * Snowball JavaScript Library v0.3 11 | * http://code.google.com/p/urim/ 12 | * http://snowball.tartarus.org/ 13 | * 14 | * Copyright 2010, Oleg Mazko 15 | * http://www.mozilla.org/MPL/ 16 | */ 17 | 18 | !function(e,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.ro=function(){this.pipeline.reset(),this.pipeline.add(e.ro.trimmer,e.ro.stopWordFilter,e.ro.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ro.stemmer))},e.ro.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.ro.trimmer=e.trimmerSupport.generateTrimmer(e.ro.wordCharacters),e.Pipeline.registerFunction(e.ro.trimmer,"trimmer-ro"),e.ro.stemmer=function(){var i=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,n=new function(){function e(e,i){L.eq_s(1,e)&&(L.ket=L.cursor,L.in_grouping(W,97,259)&&L.slice_from(i))}function n(){for(var i,r;;){if(i=L.cursor,L.in_grouping(W,97,259)&&(r=L.cursor,L.bra=r,e("u","U"),L.cursor=r,e("i","I")),L.cursor=i,L.cursor>=L.limit)break;L.cursor++}}function t(){if(L.out_grouping(W,97,259)){for(;!L.in_grouping(W,97,259);){if(L.cursor>=L.limit)return!0;L.cursor++}return!1}return!0}function a(){if(L.in_grouping(W,97,259))for(;!L.out_grouping(W,97,259);){if(L.cursor>=L.limit)return!0;L.cursor++}return!1}function o(){var e,i,r=L.cursor;if(L.in_grouping(W,97,259)){if(e=L.cursor,!t())return void(h=L.cursor);if(L.cursor=e,!a())return void(h=L.cursor)}L.cursor=r,L.out_grouping(W,97,259)&&(i=L.cursor,t()&&(L.cursor=i,L.in_grouping(W,97,259)&&L.cursor=L.limit)return!1;L.cursor++}for(;!L.out_grouping(W,97,259);){if(L.cursor>=L.limit)return!1;L.cursor++}return!0}function c(){var e=L.cursor;h=L.limit,k=h,g=h,o(),L.cursor=e,u()&&(k=L.cursor,u()&&(g=L.cursor))}function s(){for(var e;;){if(L.bra=L.cursor,e=L.find_among(z,3))switch(L.ket=L.cursor,e){case 1:L.slice_from("i");continue;case 2:L.slice_from("u");continue;case 3:if(L.cursor>=L.limit)break;L.cursor++;continue}break}}function w(){return h<=L.cursor}function m(){return k<=L.cursor}function l(){return g<=L.cursor}function f(){var e,i;if(L.ket=L.cursor,(e=L.find_among_b(C,16))&&(L.bra=L.cursor,m()))switch(e){case 1:L.slice_del();break;case 2:L.slice_from("a");break;case 3:L.slice_from("e");break;case 4:L.slice_from("i");break;case 5:i=L.limit-L.cursor,L.eq_s_b(2,"ab")||(L.cursor=L.limit-i,L.slice_from("i"));break;case 6:L.slice_from("at");break;case 7:L.slice_from("aţi")}}function p(){var e,i=L.limit-L.cursor;if(L.ket=L.cursor,(e=L.find_among_b(P,46))&&(L.bra=L.cursor,m())){switch(e){case 1:L.slice_from("abil");break;case 2:L.slice_from("ibil");break;case 3:L.slice_from("iv");break;case 4:L.slice_from("ic");break;case 5:L.slice_from("at");break;case 6:L.slice_from("it")}return _=!0,L.cursor=L.limit-i,!0}return!1}function d(){var e,i;for(_=!1;;)if(i=L.limit-L.cursor,!p()){L.cursor=L.limit-i;break}if(L.ket=L.cursor,(e=L.find_among_b(F,62))&&(L.bra=L.cursor,l())){switch(e){case 1:L.slice_del();break;case 2:L.eq_s_b(1,"ţ")&&(L.bra=L.cursor,L.slice_from("t"));break;case 3:L.slice_from("ist")}_=!0}}function b(){var e,i,r;if(L.cursor>=h){if(i=L.limit_backward,L.limit_backward=h,L.ket=L.cursor,e=L.find_among_b(q,94))switch(L.bra=L.cursor,e){case 1:if(r=L.limit-L.cursor,!L.out_grouping_b(W,97,259)&&(L.cursor=L.limit-r,!L.eq_s_b(1,"u")))break;case 2:L.slice_del()}L.limit_backward=i}}function v(){var e;L.ket=L.cursor,(e=L.find_among_b(S,5))&&(L.bra=L.cursor,w()&&1==e&&L.slice_del())}var _,g,k,h,z=[new i("",-1,3),new i("I",0,1),new i("U",0,2)],C=[new i("ea",-1,3),new i("aţia",-1,7),new i("aua",-1,2),new i("iua",-1,4),new i("aţie",-1,7),new i("ele",-1,3),new i("ile",-1,5),new i("iile",6,4),new i("iei",-1,4),new i("atei",-1,6),new i("ii",-1,4),new i("ului",-1,1),new i("ul",-1,1),new i("elor",-1,3),new i("ilor",-1,4),new i("iilor",14,4)],P=[new i("icala",-1,4),new i("iciva",-1,4),new i("ativa",-1,5),new i("itiva",-1,6),new i("icale",-1,4),new i("aţiune",-1,5),new i("iţiune",-1,6),new i("atoare",-1,5),new i("itoare",-1,6),new i("ătoare",-1,5),new i("icitate",-1,4),new i("abilitate",-1,1),new i("ibilitate",-1,2),new i("ivitate",-1,3),new i("icive",-1,4),new i("ative",-1,5),new i("itive",-1,6),new i("icali",-1,4),new i("atori",-1,5),new i("icatori",18,4),new i("itori",-1,6),new i("ători",-1,5),new i("icitati",-1,4),new i("abilitati",-1,1),new i("ivitati",-1,3),new i("icivi",-1,4),new i("ativi",-1,5),new i("itivi",-1,6),new i("icităi",-1,4),new i("abilităi",-1,1),new i("ivităi",-1,3),new i("icităţi",-1,4),new i("abilităţi",-1,1),new i("ivităţi",-1,3),new i("ical",-1,4),new i("ator",-1,5),new i("icator",35,4),new i("itor",-1,6),new i("ător",-1,5),new i("iciv",-1,4),new i("ativ",-1,5),new i("itiv",-1,6),new i("icală",-1,4),new i("icivă",-1,4),new i("ativă",-1,5),new i("itivă",-1,6)],F=[new i("ica",-1,1),new i("abila",-1,1),new i("ibila",-1,1),new i("oasa",-1,1),new i("ata",-1,1),new i("ita",-1,1),new i("anta",-1,1),new i("ista",-1,3),new i("uta",-1,1),new i("iva",-1,1),new i("ic",-1,1),new i("ice",-1,1),new i("abile",-1,1),new i("ibile",-1,1),new i("isme",-1,3),new i("iune",-1,2),new i("oase",-1,1),new i("ate",-1,1),new i("itate",17,1),new i("ite",-1,1),new i("ante",-1,1),new i("iste",-1,3),new i("ute",-1,1),new i("ive",-1,1),new i("ici",-1,1),new i("abili",-1,1),new i("ibili",-1,1),new i("iuni",-1,2),new i("atori",-1,1),new i("osi",-1,1),new i("ati",-1,1),new i("itati",30,1),new i("iti",-1,1),new i("anti",-1,1),new i("isti",-1,3),new i("uti",-1,1),new i("işti",-1,3),new i("ivi",-1,1),new i("ităi",-1,1),new i("oşi",-1,1),new i("ităţi",-1,1),new i("abil",-1,1),new i("ibil",-1,1),new i("ism",-1,3),new i("ator",-1,1),new i("os",-1,1),new i("at",-1,1),new i("it",-1,1),new i("ant",-1,1),new i("ist",-1,3),new i("ut",-1,1),new i("iv",-1,1),new i("ică",-1,1),new i("abilă",-1,1),new i("ibilă",-1,1),new i("oasă",-1,1),new i("ată",-1,1),new i("ită",-1,1),new i("antă",-1,1),new i("istă",-1,3),new i("ută",-1,1),new i("ivă",-1,1)],q=[new i("ea",-1,1),new i("ia",-1,1),new i("esc",-1,1),new i("ăsc",-1,1),new i("ind",-1,1),new i("ând",-1,1),new i("are",-1,1),new i("ere",-1,1),new i("ire",-1,1),new i("âre",-1,1),new i("se",-1,2),new i("ase",10,1),new i("sese",10,2),new i("ise",10,1),new i("use",10,1),new i("âse",10,1),new i("eşte",-1,1),new i("ăşte",-1,1),new i("eze",-1,1),new i("ai",-1,1),new i("eai",19,1),new i("iai",19,1),new i("sei",-1,2),new i("eşti",-1,1),new i("ăşti",-1,1),new i("ui",-1,1),new i("ezi",-1,1),new i("âi",-1,1),new i("aşi",-1,1),new i("seşi",-1,2),new i("aseşi",29,1),new i("seseşi",29,2),new i("iseşi",29,1),new i("useşi",29,1),new i("âseşi",29,1),new i("işi",-1,1),new i("uşi",-1,1),new i("âşi",-1,1),new i("aţi",-1,2),new i("eaţi",38,1),new i("iaţi",38,1),new i("eţi",-1,2),new i("iţi",-1,2),new i("âţi",-1,2),new i("arăţi",-1,1),new i("serăţi",-1,2),new i("aserăţi",45,1),new i("seserăţi",45,2),new i("iserăţi",45,1),new i("userăţi",45,1),new i("âserăţi",45,1),new i("irăţi",-1,1),new i("urăţi",-1,1),new i("ârăţi",-1,1),new i("am",-1,1),new i("eam",54,1),new i("iam",54,1),new i("em",-1,2),new i("asem",57,1),new i("sesem",57,2),new i("isem",57,1),new i("usem",57,1),new i("âsem",57,1),new i("im",-1,2),new i("âm",-1,2),new i("ăm",-1,2),new i("arăm",65,1),new i("serăm",65,2),new i("aserăm",67,1),new i("seserăm",67,2),new i("iserăm",67,1),new i("userăm",67,1),new i("âserăm",67,1),new i("irăm",65,1),new i("urăm",65,1),new i("ârăm",65,1),new i("au",-1,1),new i("eau",76,1),new i("iau",76,1),new i("indu",-1,1),new i("ându",-1,1),new i("ez",-1,1),new i("ească",-1,1),new i("ară",-1,1),new i("seră",-1,2),new i("aseră",84,1),new i("seseră",84,2),new i("iseră",84,1),new i("useră",84,1),new i("âseră",84,1),new i("iră",-1,1),new i("ură",-1,1),new i("âră",-1,1),new i("ează",-1,1)],S=[new i("a",-1,1),new i("e",-1,1),new i("ie",1,1),new i("i",-1,1),new i("ă",-1,1)],W=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,2,32,0,0,4],L=new r;this.setCurrent=function(e){L.setCurrent(e)},this.getCurrent=function(){return L.getCurrent()},this.stem=function(){var e=L.cursor;return n(),L.cursor=e,c(),L.limit_backward=e,L.cursor=L.limit,f(),L.cursor=L.limit,d(),L.cursor=L.limit,_||(L.cursor=L.limit,b(),L.cursor=L.limit),v(),L.cursor=L.limit_backward,s(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.ro.stemmer,"stemmer-ro"),e.ro.stopWordFilter=e.generateStopWordFilter("acea aceasta această aceea acei aceia acel acela acele acelea acest acesta aceste acestea aceşti aceştia acolo acord acum ai aia aibă aici al ale alea altceva altcineva am ar are asemenea asta astea astăzi asupra au avea avem aveţi azi aş aşadar aţi bine bucur bună ca care caut ce cel ceva chiar cinci cine cineva contra cu cum cumva curând curînd când cât câte câtva câţi cînd cît cîte cîtva cîţi că căci cărei căror cărui către da dacă dar datorită dată dau de deci deja deoarece departe deşi din dinaintea dintr- dintre doi doilea două drept după dă ea ei el ele eram este eu eşti face fata fi fie fiecare fii fim fiu fiţi frumos fără graţie halbă iar ieri la le li lor lui lângă lîngă mai mea mei mele mereu meu mi mie mine mult multă mulţi mulţumesc mâine mîine mă ne nevoie nici nicăieri nimeni nimeri nimic nişte noastre noastră noi noroc nostru nouă noştri nu opt ori oricare orice oricine oricum oricând oricât oricînd oricît oriunde patra patru patrulea pe pentru peste pic poate pot prea prima primul prin puţin puţina puţină până pînă rog sa sale sau se spate spre sub sunt suntem sunteţi sută sînt sîntem sînteţi să săi său ta tale te timp tine toate toată tot totuşi toţi trei treia treilea tu tăi tău un una unde undeva unei uneia unele uneori unii unor unora unu unui unuia unul vi voastre voastră voi vostru vouă voştri vreme vreo vreun vă zece zero zi zice îi îl îmi împotriva în înainte înaintea încotro încât încît între întrucât întrucît îţi ăla ălea ăsta ăstea ăştia şapte şase şi ştiu ţi ţie".split(" ")),e.Pipeline.registerFunction(e.ro.stopWordFilter,"stopWordFilter-ro")}}); --------------------------------------------------------------------------------