diff --git a/README.md b/README.md index 37c127d1..53b11a3a 100644 --- a/README.md +++ b/README.md @@ -306,6 +306,7 @@ PORT | port number to listen on for Grist server REDIS_URL | optional redis server for browser sessions and db query caching GRIST_SNAPSHOT_TIME_CAP | optional. Define the caps for tracking buckets. Usage: {"hour": 25, "day": 32, "isoWeek": 12, "month": 96, "year": 1000} GRIST_SNAPSHOT_KEEP | optional. Number of recent snapshots to retain unconditionally for a document, regardless of when they were made +GRIST_PROMCLIENT_PORT | optional. If set, serve the Prometheus metrics on the specified port number. ⚠️ Be sure to use a port which is not publicly exposed ⚠️. #### AI Formula Assistant related variables (all optional): diff --git a/package.json b/package.json index e8ca28d3..815b5c29 100644 --- a/package.json +++ b/package.json @@ -173,6 +173,7 @@ "plotly.js-basic-dist": "2.13.2", "popper-max-size-modifier": "0.2.0", "popweasel": "0.1.20", + "prom-client": "14.2.0", "qrcode": "1.5.0", "randomcolor": "0.5.3", "redis": "3.1.1", diff --git a/static/locales/de.client.json b/static/locales/de.client.json index ae2ecadf..ce8c970f 100644 --- a/static/locales/de.client.json +++ b/static/locales/de.client.json @@ -430,7 +430,27 @@ "Unfreeze {{count}} columns_one": "Diese Spalte entsperren", "Unfreeze {{count}} columns_other": "{{count}} Spalten entsperren", "Insert column to the right": "Spalte rechts einfügen", - "Insert column to the left": "Spalte links einfügen" + "Insert column to the left": "Spalte links einfügen", + "Shortcuts": "Abkürzungen", + "Show hidden columns": "Ausgeblendete Spalten anzeigen", + "Created At": "Erstellt am", + "Authorship": "Urheberschaft", + "Last Updated By": "Zuletzt aktualisiert von", + "Hidden Columns": "Ausgeblendete Spalten", + "Lookups": "Nachschlagen", + "Apply on record changes": "Auf Datensatzänderungen anwenden", + "Created By": "Erstellt von", + "Last Updated At": "Zuletzt aktualisiert am", + "Apply to new records": "Auf neue Datensätze anwenden", + "Timestamp": "Zeitstempel", + "no reference column": "keine Referenzspalte", + "Detect Duplicates in...": "Duplikate erkennen in...", + "UUID": "UUID", + "No reference columns.": "Keine Referenzspalten.", + "Duplicate in {{- label}}": "Duplikate in {{- label}}", + "Search columns": "Spalten suchen", + "Adding UUID column": "Hinzufügen der UUID-Spalte", + "Adding duplicates column": "Hinzufügen einer Duplikatspalte" }, "GristDoc": { "Added new linked section to view {{viewName}}": "Neuer verlinkter Abschnitt zur Ansicht hinzugefügt {{viewName}}", @@ -876,7 +896,8 @@ "Text": "Text", "Integer": "Ganze Zahl", "Toggle": "Umschalten", - "Date": "Datum" + "Date": "Datum", + "Search columns": "Spalten suchen" }, "modals": { "Cancel": "Abbrechen", diff --git a/static/locales/en.client.json b/static/locales/en.client.json index 7a5157be..d8b71f16 100644 --- a/static/locales/en.client.json +++ b/static/locales/en.client.json @@ -412,7 +412,14 @@ "Shortcuts": "Shortcuts", "Show hidden columns": "Show hidden columns", "Timestamp": "Timestamp", - "no reference column": "no reference column" + "no reference column": "no reference column", + "Adding UUID column": "Adding UUID column", + "Adding duplicates column": "Adding duplicates column", + "Detect Duplicates in...": "Detect Duplicates in...", + "Duplicate in {{- label}}": "Duplicate in {{- label}}", + "No reference columns.": "No reference columns.", + "Search columns": "Search columns", + "UUID": "UUID" }, "GristDoc": { "Added new linked section to view {{viewName}}": "Added new linked section to view {{viewName}}", diff --git a/static/locales/es.client.json b/static/locales/es.client.json index 26f5636e..25d1373b 100644 --- a/static/locales/es.client.json +++ b/static/locales/es.client.json @@ -370,7 +370,15 @@ "Last Updated At": "Última actualización en", "Apply to new records": "Aplicar a los nuevos registros", "Timestamp": "Marca temporal", - "no reference column": "Sin columna de referencia" + "no reference column": "Sin columna de referencia", + "Shortcuts": "Atajos", + "Detect Duplicates in...": "Detectar duplicados en...", + "UUID": "UUID", + "No reference columns.": "Sin columnas de referencia.", + "Duplicate in {{- label}}": "Duplicado en {{- label}}", + "Search columns": "Buscar columnas", + "Adding UUID column": "Añadiendo una columna UUID", + "Adding duplicates column": "Añadiendo la columna de duplicados" }, "HomeIntro": { ", or find an expert via our ": ", o encontrar un experto a través de nuestro", diff --git a/static/locales/fr.client.json b/static/locales/fr.client.json index dd0b0b29..164e28c0 100644 --- a/static/locales/fr.client.json +++ b/static/locales/fr.client.json @@ -396,7 +396,27 @@ "Sorted (#{{count}})_one": "Trié (#{{count}})", "Sorted (#{{count}})_other": "Triés (#{{count}})", "Insert column to the right": "Insérer une colonne à droite", - "Insert column to the left": "Insérer une colonne à gauche" + "Insert column to the left": "Insérer une colonne à gauche", + "Detect Duplicates in...": "Détecter les duplicas dans...", + "UUID": "UUID", + "Shortcuts": "Raccourcis", + "Show hidden columns": "Montrer les colonnes cachées", + "Created At": "Créé(e) le", + "Authorship": "Paternité", + "Last Updated By": "Dernière mise à jour par", + "Hidden Columns": "Colonnes cachées", + "Lookups": "Champ rapporté", + "No reference columns.": "Pas de colonne de référence.", + "Apply on record changes": "Appliquer lors de changements d'enregistrements", + "Duplicate in {{- label}}": "Duplica dans {{-label}}", + "Created By": "Créé(e) par", + "Last Updated At": "Dernière mise à jour le", + "Apply to new records": "Appliquer au nouveaux enregistrements", + "Search columns": "Chercher des colonnes", + "Timestamp": "Horodatage", + "no reference column": "pas de colonne de référence", + "Adding UUID column": "Ajout d'une colonne UUID", + "Adding duplicates column": "Ajouter des colonnes dupliquées" }, "GristDoc": { "Import from file": "Importer depuis un fichier", @@ -814,7 +834,8 @@ "Choice List": "Choix multiple", "Toggle": "Booléen", "Reference": "Référence", - "Any": "Non défini" + "Any": "Non défini", + "Search columns": "Chercher des colonnes" }, "modals": { "Save": "Enregistrer", diff --git a/static/locales/pl.client.json b/static/locales/pl.client.json index 2b9ddd47..6cc9c296 100644 --- a/static/locales/pl.client.json +++ b/static/locales/pl.client.json @@ -412,7 +412,7 @@ "HomeLeftPane": { "Delete": "Usuń", "Delete {{workspace}} and all included documents?": "Usunąć {{workspace}} i wszystkie dołączone dokumenty?", - "Examples & Templates": "szablony", + "Examples & Templates": "Szablony", "All Documents": "Wszystkie dokumenty", "Create Empty Document": "Utwórz pusty dokument", "Create Workspace": "Utwórz przestrzeń roboczą", diff --git a/static/locales/pt_BR.client.json b/static/locales/pt_BR.client.json index 75876977..345dd871 100644 --- a/static/locales/pt_BR.client.json +++ b/static/locales/pt_BR.client.json @@ -430,7 +430,27 @@ "Unfreeze {{count}} columns_one": "Descongelar esta coluna", "Unfreeze {{count}} columns_other": "Descongelar {{count}} colunas", "Insert column to the left": "Inserir coluna à esquerda", - "Insert column to the right": "Inserir coluna à direita" + "Insert column to the right": "Inserir coluna à direita", + "Shortcuts": "Atalhos", + "Show hidden columns": "Mostrar colunas ocultas", + "Created At": "Criado em", + "Authorship": "Autoria", + "Last Updated By": "Última atualização por", + "Hidden Columns": "Colunas ocultas", + "Lookups": "Pesquisas", + "Apply on record changes": "Aplicar em alterações de registro", + "Created By": "Criado por", + "Last Updated At": "Última atualização em", + "Apply to new records": "Aplicar a novos registros", + "Timestamp": "Carimbo de data/hora", + "no reference column": "nenhuma coluna de referência", + "Detect Duplicates in...": "Detectar duplicatas em...", + "UUID": "UUID", + "No reference columns.": "Não há colunas de referência.", + "Duplicate in {{- label}}": "Duplicar em {{- label}}", + "Search columns": "Procurar colunas", + "Adding UUID column": "Adicionando coluna UUID", + "Adding duplicates column": "Adicionar coluna duplicatas" }, "GristDoc": { "Added new linked section to view {{viewName}}": "Adicionada nova seção vinculada para visualizar {{viewName}}}", @@ -876,7 +896,8 @@ "Attachment": "Anexo", "Any": "Qualquer", "Choice": "Opção", - "Reference": "Referência" + "Reference": "Referência", + "Search columns": "Procurar colunas" }, "modals": { "Cancel": "Cancelar", diff --git a/static/locales/ru.client.json b/static/locales/ru.client.json index c8fc3664..670a9eed 100644 --- a/static/locales/ru.client.json +++ b/static/locales/ru.client.json @@ -548,7 +548,15 @@ "Created By": "Создатель", "Last Updated At": "Последнее обновление в", "Apply to new records": "Применять к новым записям", - "no reference column": "нет ссылочного столбца" + "no reference column": "нет ссылочного столбца", + "Detect Duplicates in...": "Обнаружение дубликатов в...", + "UUID": "UUID", + "No reference columns.": "Нет ссылочных столбцов.", + "Duplicate in {{- label}}": "Дублирование в {{- label}}", + "Search columns": "Поисковые столбцы", + "Timestamp": "Метка времени", + "Adding UUID column": "Добавление столбца UUID", + "Adding duplicates column": "Добавление столбца дубликатов" }, "FilterBar": { "SearchColumns": "Столбцы поиска", diff --git a/static/locales/sl.client.json b/static/locales/sl.client.json index b9b9bc60..3d8f0db2 100644 --- a/static/locales/sl.client.json +++ b/static/locales/sl.client.json @@ -205,7 +205,27 @@ "Unfreeze {{count}} columns_other": "Odmrznite {{count}} stolpcev", "Insert column to the right": "Vstavi stolpec na desno", "Reset {{count}} entire columns_one": "Ponastavi celoten stolpec", - "Insert column to the left": "Vstavi stolpec na levo" + "Insert column to the left": "Vstavi stolpec na levo", + "Shortcuts": "Bližnjice", + "Show hidden columns": "Prikaži skrite stolpce", + "Created At": "Ustvarjeno pri", + "Authorship": "Avtorstvo", + "Last Updated By": "Nazadnje posodobil", + "Hidden Columns": "Skriti stolpci", + "Lookups": "Iskanje", + "Apply on record changes": "Uporabi na spremenjenih zapisih", + "Created By": "Ustvaril", + "Last Updated At": "Nazadnje posodobljeno ob", + "Apply to new records": "Uporabi na novih zapisih", + "Timestamp": "Časovni žig", + "no reference column": "ni referenčnega stolpca", + "Detect Duplicates in...": "Zaznaj dvojnike v ...", + "UUID": "UUID", + "No reference columns.": "Ni referenčnih stolpcev.", + "Duplicate in {{- label}}": "Dvojnik v {{- label}}", + "Search columns": "Preišči stolpce", + "Adding UUID column": "Dodajanje UUID stolpca", + "Adding duplicates column": "Dodajanje podvojenega stolpca" }, "HomeLeftPane": { "Trash": "Koš", @@ -995,7 +1015,8 @@ "Toggle": "Preklopi", "Choice List": "Izbirni seznam", "Any": "Katerikoli", - "DateTime": "Datum čas" + "DateTime": "Datum čas", + "Search columns": "Iskalni stolpci" }, "errorPages": { "Something went wrong": "Nekaj je šlo narobe", diff --git a/stubs/app/server/prometheus-exporter.ts b/stubs/app/server/prometheus-exporter.ts new file mode 100644 index 00000000..7bf97d33 --- /dev/null +++ b/stubs/app/server/prometheus-exporter.ts @@ -0,0 +1,25 @@ +import { collectDefaultMetrics, register } from 'prom-client'; +import http from 'http'; + +const reqListener = (req: http.IncomingMessage, res: http.ServerResponse) => { + register.metrics().then((metrics) => { + res.writeHead(200, { 'Content-Type': register.contentType }); + res.end(metrics); + }).catch((e) => { + res.writeHead(500); + res.end(e.message); + }); +}; + +export function runPrometheusExporter(port: number) { + collectDefaultMetrics(); + + if (isNaN(port)) { + throw new Error(`Invalid port: ${process.env.GRIST_PROMCLIENT_PORT}`); + } + const server = http.createServer(reqListener); + server.listen(port, '0.0.0.0'); + + console.log(`Prometheus exporter listening on port ${port}.`); + return server; +} diff --git a/stubs/app/server/server.ts b/stubs/app/server/server.ts index 34f91d07..4f5aa2e2 100644 --- a/stubs/app/server/server.ts +++ b/stubs/app/server/server.ts @@ -36,6 +36,7 @@ setDefaultEnv('GRIST_WIDGET_LIST_URL', commonUrls.gristLabsWidgetRepository); import {updateDb} from 'app/server/lib/dbUtils'; import {main as mergedServerMain, parseServerTypes} from 'app/server/mergedServerMain'; import * as fse from 'fs-extra'; +import {runPrometheusExporter} from './prometheus-exporter'; const G = { port: parseInt(process.env.PORT!, 10) || 8484, @@ -102,6 +103,10 @@ export async function main() { console.log('For full logs, re-run with DEBUG=1'); } + if (process.env.GRIST_PROMCLIENT_PORT) { + runPrometheusExporter(parseInt(process.env.GRIST_PROMCLIENT_PORT, 10)); + } + // If SAML is not configured, there's no login system, so provide a default email address. setDefaultEnv('GRIST_DEFAULT_EMAIL', 'you@example.com'); // Set directory for uploaded documents. diff --git a/yarn.lock b/yarn.lock index 7ff70058..9349d7f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1739,6 +1739,11 @@ binary@~0.3.0: buffers "~0.1.1" chainsaw "~0.1.0" +bintrees@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.2.tgz#49f896d6e858a4a499df85c38fb399b9aff840f8" + integrity sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw== + bl@^4.0.3: version "4.1.0" resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz" @@ -6520,6 +6525,13 @@ process@~0.11.0: resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= +prom-client@14.2.0: + version "14.2.0" + resolved "https://registry.yarnpkg.com/prom-client/-/prom-client-14.2.0.tgz#ca94504e64156f6506574c25fb1c34df7812cf11" + integrity sha512-sF308EhTenb/pDRPakm+WgiN+VdM/T1RaHj1x+MvAuT8UiQP8JmOEbxVqtkbfR4LrvOg5n7ic01kRBDGXjYikA== + dependencies: + tdigest "^0.1.1" + promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" @@ -7688,6 +7700,13 @@ tar@^6.0.2, tar@^6.1.11, tar@^6.1.2: mkdirp "^1.0.3" yallist "^4.0.0" +tdigest@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/tdigest/-/tdigest-0.1.2.tgz#96c64bac4ff10746b910b0e23b515794e12faced" + integrity sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA== + dependencies: + bintrees "1.0.2" + term-size@^2.1.0: version "2.2.0" resolved "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz"