From e800d6c41df085853cb3c660ae6dcb75c0da99e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Choutri?= Date: Sat, 26 Mar 2022 16:47:31 +0100 Subject: [PATCH] Integrate with log-base more tightly --- .gitignore | 1 + Makefile | 6 +- cbits/categorise.cpp | 1137 ------------------ environment.sh | 3 + flora.cabal | 18 +- src/Flora/Environment.hs | 24 +- src/Flora/Model/UserSession.hs | 52 - src/FloraWeb/Server.hs | 23 +- src/FloraWeb/Server/Auth.hs | 14 +- src/FloraWeb/Server/Logging.hs | 39 + src/FloraWeb/Server/{Logging => }/Metrics.hs | 2 +- src/FloraWeb/Server/{Logging => }/Tracing.hs | 2 +- 12 files changed, 105 insertions(+), 1216 deletions(-) delete mode 100644 cbits/categorise.cpp delete mode 100644 src/Flora/Model/UserSession.hs create mode 100644 src/FloraWeb/Server/Logging.hs rename src/FloraWeb/Server/{Logging => }/Metrics.hs (98%) rename src/FloraWeb/Server/{Logging => }/Tracing.hs (97%) diff --git a/.gitignore b/.gitignore index 233022ed..32330343 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ assets/node_modules assets/yarn-error.log assets/fonts result* +cbits/*.cpp diff --git a/Makefile b/Makefile index 575ce185..c97d79c4 100644 --- a/Makefile +++ b/Makefile @@ -42,13 +42,13 @@ ghci: repl ## Start a cabal REPL (alias for `make repl`) watch: soufflé ## Load the main library and reload on file change @ghcid --target flora-server -l -test: ## Run the test suite +test : soufflé ## Run the test suite ./scripts/run-tests.sh -watch-test: ## Load the tests in ghcid and reload them on file change +watch-test: soufflé ## Load the tests in ghcid and reload them on file change ./scripts/run-tests.sh --watch -watch-server: ## Start flora-server in ghcid +watch-server: soufflé ## Start flora-server in ghcid @ghcid --target=flora-server --restart="src" --test 'FloraWeb.Server.runFlora' lint: ## Run the code linter (HLint) diff --git a/cbits/categorise.cpp b/cbits/categorise.cpp deleted file mode 100644 index e8f4a492..00000000 --- a/cbits/categorise.cpp +++ /dev/null @@ -1,1137 +0,0 @@ - -#include "souffle/CompiledSouffle.h" - -extern "C" { -} - -namespace souffle { -static const RamDomain RAM_BIT_SHIFT_MASK = RAM_DOMAIN_SIZE - 1; -struct t_btree_iii__0_1_2__111 { -static constexpr Relation::arity_type Arity = 3; -using t_tuple = Tuple; -struct t_comparator_0{ - int operator()(const t_tuple& a, const t_tuple& b) const { - return (ramBitCast(a[0]) < ramBitCast(b[0])) ? -1 : (ramBitCast(a[0]) > ramBitCast(b[0])) ? 1 :((ramBitCast(a[1]) < ramBitCast(b[1])) ? -1 : (ramBitCast(a[1]) > ramBitCast(b[1])) ? 1 :((ramBitCast(a[2]) < ramBitCast(b[2])) ? -1 : (ramBitCast(a[2]) > ramBitCast(b[2])) ? 1 :(0))); - } -bool less(const t_tuple& a, const t_tuple& b) const { - return (ramBitCast(a[0]) < ramBitCast(b[0]))|| (ramBitCast(a[0]) == ramBitCast(b[0])) && ((ramBitCast(a[1]) < ramBitCast(b[1]))|| (ramBitCast(a[1]) == ramBitCast(b[1])) && ((ramBitCast(a[2]) < ramBitCast(b[2])))); - } -bool equal(const t_tuple& a, const t_tuple& b) const { -return (ramBitCast(a[0]) == ramBitCast(b[0]))&&(ramBitCast(a[1]) == ramBitCast(b[1]))&&(ramBitCast(a[2]) == ramBitCast(b[2])); - } -}; -using t_ind_0 = btree_set; -t_ind_0 ind_0; -using iterator = t_ind_0::iterator; -struct context { -t_ind_0::operation_hints hints_0_lower; -t_ind_0::operation_hints hints_0_upper; -}; -context createContext() { return context(); } -bool insert(const t_tuple& t) { -context h; -return insert(t, h); -} -bool insert(const t_tuple& t, context& h) { -if (ind_0.insert(t, h.hints_0_lower)) { -return true; -} else return false; -} -bool insert(const RamDomain* ramDomain) { -RamDomain data[3]; -std::copy(ramDomain, ramDomain + 3, data); -const t_tuple& tuple = reinterpret_cast(data); -context h; -return insert(tuple, h); -} -bool insert(RamDomain a0,RamDomain a1,RamDomain a2) { -RamDomain data[3] = {a0,a1,a2}; -return insert(data); -} -bool contains(const t_tuple& t, context& h) const { -return ind_0.contains(t, h.hints_0_lower); -} -bool contains(const t_tuple& t) const { -context h; -return contains(t, h); -} -std::size_t size() const { -return ind_0.size(); -} -iterator find(const t_tuple& t, context& h) const { -return ind_0.find(t, h.hints_0_lower); -} -iterator find(const t_tuple& t) const { -context h; -return find(t, h); -} -range lowerUpperRange_000(const t_tuple& /* lower */, const t_tuple& /* upper */, context& /* h */) const { -return range(ind_0.begin(),ind_0.end()); -} -range lowerUpperRange_000(const t_tuple& /* lower */, const t_tuple& /* upper */) const { -return range(ind_0.begin(),ind_0.end()); -} -range lowerUpperRange_111(const t_tuple& lower, const t_tuple& upper, context& h) const { -t_comparator_0 comparator; -int cmp = comparator(lower, upper); -if (cmp == 0) { - auto pos = ind_0.find(lower, h.hints_0_lower); - auto fin = ind_0.end(); - if (pos != fin) {fin = pos; ++fin;} - return make_range(pos, fin); -} -if (cmp > 0) { - return make_range(ind_0.end(), ind_0.end()); -} -return make_range(ind_0.lower_bound(lower, h.hints_0_lower), ind_0.upper_bound(upper, h.hints_0_upper)); -} -range lowerUpperRange_111(const t_tuple& lower, const t_tuple& upper) const { -context h; -return lowerUpperRange_111(lower,upper,h); -} -bool empty() const { -return ind_0.empty(); -} -std::vector> partition() const { -return ind_0.getChunks(400); -} -void purge() { -ind_0.clear(); -} -iterator begin() const { -return ind_0.begin(); -} -iterator end() const { -return ind_0.end(); -} -void printStatistics(std::ostream& o) const { -o << " arity 3 direct b-tree index 0 lex-order [0,1,2]\n"; -ind_0.printStats(o); -} -}; -struct t_btree_ii__0_1__11__10 { -static constexpr Relation::arity_type Arity = 2; -using t_tuple = Tuple; -struct t_comparator_0{ - int operator()(const t_tuple& a, const t_tuple& b) const { - return (ramBitCast(a[0]) < ramBitCast(b[0])) ? -1 : (ramBitCast(a[0]) > ramBitCast(b[0])) ? 1 :((ramBitCast(a[1]) < ramBitCast(b[1])) ? -1 : (ramBitCast(a[1]) > ramBitCast(b[1])) ? 1 :(0)); - } -bool less(const t_tuple& a, const t_tuple& b) const { - return (ramBitCast(a[0]) < ramBitCast(b[0]))|| (ramBitCast(a[0]) == ramBitCast(b[0])) && ((ramBitCast(a[1]) < ramBitCast(b[1]))); - } -bool equal(const t_tuple& a, const t_tuple& b) const { -return (ramBitCast(a[0]) == ramBitCast(b[0]))&&(ramBitCast(a[1]) == ramBitCast(b[1])); - } -}; -using t_ind_0 = btree_set; -t_ind_0 ind_0; -using iterator = t_ind_0::iterator; -struct context { -t_ind_0::operation_hints hints_0_lower; -t_ind_0::operation_hints hints_0_upper; -}; -context createContext() { return context(); } -bool insert(const t_tuple& t) { -context h; -return insert(t, h); -} -bool insert(const t_tuple& t, context& h) { -if (ind_0.insert(t, h.hints_0_lower)) { -return true; -} else return false; -} -bool insert(const RamDomain* ramDomain) { -RamDomain data[2]; -std::copy(ramDomain, ramDomain + 2, data); -const t_tuple& tuple = reinterpret_cast(data); -context h; -return insert(tuple, h); -} -bool insert(RamDomain a0,RamDomain a1) { -RamDomain data[2] = {a0,a1}; -return insert(data); -} -bool contains(const t_tuple& t, context& h) const { -return ind_0.contains(t, h.hints_0_lower); -} -bool contains(const t_tuple& t) const { -context h; -return contains(t, h); -} -std::size_t size() const { -return ind_0.size(); -} -iterator find(const t_tuple& t, context& h) const { -return ind_0.find(t, h.hints_0_lower); -} -iterator find(const t_tuple& t) const { -context h; -return find(t, h); -} -range lowerUpperRange_00(const t_tuple& /* lower */, const t_tuple& /* upper */, context& /* h */) const { -return range(ind_0.begin(),ind_0.end()); -} -range lowerUpperRange_00(const t_tuple& /* lower */, const t_tuple& /* upper */) const { -return range(ind_0.begin(),ind_0.end()); -} -range lowerUpperRange_11(const t_tuple& lower, const t_tuple& upper, context& h) const { -t_comparator_0 comparator; -int cmp = comparator(lower, upper); -if (cmp == 0) { - auto pos = ind_0.find(lower, h.hints_0_lower); - auto fin = ind_0.end(); - if (pos != fin) {fin = pos; ++fin;} - return make_range(pos, fin); -} -if (cmp > 0) { - return make_range(ind_0.end(), ind_0.end()); -} -return make_range(ind_0.lower_bound(lower, h.hints_0_lower), ind_0.upper_bound(upper, h.hints_0_upper)); -} -range lowerUpperRange_11(const t_tuple& lower, const t_tuple& upper) const { -context h; -return lowerUpperRange_11(lower,upper,h); -} -range lowerUpperRange_10(const t_tuple& lower, const t_tuple& upper, context& h) const { -t_comparator_0 comparator; -int cmp = comparator(lower, upper); -if (cmp > 0) { - return make_range(ind_0.end(), ind_0.end()); -} -return make_range(ind_0.lower_bound(lower, h.hints_0_lower), ind_0.upper_bound(upper, h.hints_0_upper)); -} -range lowerUpperRange_10(const t_tuple& lower, const t_tuple& upper) const { -context h; -return lowerUpperRange_10(lower,upper,h); -} -bool empty() const { -return ind_0.empty(); -} -std::vector> partition() const { -return ind_0.getChunks(400); -} -void purge() { -ind_0.clear(); -} -iterator begin() const { -return ind_0.begin(); -} -iterator end() const { -return ind_0.end(); -} -void printStatistics(std::ostream& o) const { -o << " arity 2 direct b-tree index 0 lex-order [0,1]\n"; -ind_0.printStats(o); -} -}; -struct t_btree_i__0__1 { -static constexpr Relation::arity_type Arity = 1; -using t_tuple = Tuple; -struct t_comparator_0{ - int operator()(const t_tuple& a, const t_tuple& b) const { - return (ramBitCast(a[0]) < ramBitCast(b[0])) ? -1 : (ramBitCast(a[0]) > ramBitCast(b[0])) ? 1 :(0); - } -bool less(const t_tuple& a, const t_tuple& b) const { - return (ramBitCast(a[0]) < ramBitCast(b[0])); - } -bool equal(const t_tuple& a, const t_tuple& b) const { -return (ramBitCast(a[0]) == ramBitCast(b[0])); - } -}; -using t_ind_0 = btree_set; -t_ind_0 ind_0; -using iterator = t_ind_0::iterator; -struct context { -t_ind_0::operation_hints hints_0_lower; -t_ind_0::operation_hints hints_0_upper; -}; -context createContext() { return context(); } -bool insert(const t_tuple& t) { -context h; -return insert(t, h); -} -bool insert(const t_tuple& t, context& h) { -if (ind_0.insert(t, h.hints_0_lower)) { -return true; -} else return false; -} -bool insert(const RamDomain* ramDomain) { -RamDomain data[1]; -std::copy(ramDomain, ramDomain + 1, data); -const t_tuple& tuple = reinterpret_cast(data); -context h; -return insert(tuple, h); -} -bool insert(RamDomain a0) { -RamDomain data[1] = {a0}; -return insert(data); -} -bool contains(const t_tuple& t, context& h) const { -return ind_0.contains(t, h.hints_0_lower); -} -bool contains(const t_tuple& t) const { -context h; -return contains(t, h); -} -std::size_t size() const { -return ind_0.size(); -} -iterator find(const t_tuple& t, context& h) const { -return ind_0.find(t, h.hints_0_lower); -} -iterator find(const t_tuple& t) const { -context h; -return find(t, h); -} -range lowerUpperRange_0(const t_tuple& /* lower */, const t_tuple& /* upper */, context& /* h */) const { -return range(ind_0.begin(),ind_0.end()); -} -range lowerUpperRange_0(const t_tuple& /* lower */, const t_tuple& /* upper */) const { -return range(ind_0.begin(),ind_0.end()); -} -range lowerUpperRange_1(const t_tuple& lower, const t_tuple& upper, context& h) const { -t_comparator_0 comparator; -int cmp = comparator(lower, upper); -if (cmp == 0) { - auto pos = ind_0.find(lower, h.hints_0_lower); - auto fin = ind_0.end(); - if (pos != fin) {fin = pos; ++fin;} - return make_range(pos, fin); -} -if (cmp > 0) { - return make_range(ind_0.end(), ind_0.end()); -} -return make_range(ind_0.lower_bound(lower, h.hints_0_lower), ind_0.upper_bound(upper, h.hints_0_upper)); -} -range lowerUpperRange_1(const t_tuple& lower, const t_tuple& upper) const { -context h; -return lowerUpperRange_1(lower,upper,h); -} -bool empty() const { -return ind_0.empty(); -} -std::vector> partition() const { -return ind_0.getChunks(400); -} -void purge() { -ind_0.clear(); -} -iterator begin() const { -return ind_0.begin(); -} -iterator end() const { -return ind_0.end(); -} -void printStatistics(std::ostream& o) const { -o << " arity 1 direct b-tree index 0 lex-order [0]\n"; -ind_0.printStats(o); -} -}; - -class Sf_categorise : public SouffleProgram { -private: -static inline std::string substr_wrapper(const std::string& str, std::size_t idx, std::size_t len) { - std::string result; - try { result = str.substr(idx,len); } catch(...) { - std::cerr << "warning: wrong index position provided by substr(\""; - std::cerr << str << "\"," << (int32_t)idx << "," << (int32_t)len << ") functor.\n"; - } return result; -} -public: -// -- initialize symbol table -- -SymbolTable symTable{ - R"_(algorithms)_", - R"_(Algorithms)_", - R"_(Algorithms implemented in Haskell, like sorting, searching)_", - R"_(bioinformatics)_", - R"_(Bioinformatics)_", - R"_(Tooling, algorithms and techniques for the domain of Bioinformatics)_", - R"_(command-line)_", - R"_(CLI & TUI tooling)_", - R"_(Libraries to develope command-line interfaces)_", - R"_(compilers-interpreters)_", - R"_(Compilers and Interpreters)_", - R"_(Tooling to create compilers and interpreters)_", - R"_(cryptography)_", - R"_(Cryptography)_", - R"_(Algorithms for encrypting and hashing data)_", - R"_(data-structures)_", - R"_(Data Structures)_", - R"_(Data structures, whether they are purely functional or mutable, there's always one for you)_", - R"_(databases)_", - R"_(Databases)_", - R"_(Database drivers and interfaces)_", - R"_(development)_", - R"_(Development)_", - R"_(Development helpers, integration with other languages)_", - R"_(distributed)_", - R"_(Distributed Systems & Computation)_", - R"_(Tooling and techniques for writing distributed systems)_", - R"_(ffi)_", - R"_(FFI)_", - R"_(Tooling to work with the Foreign Function Interface)_", - R"_(game-dev)_", - R"_(Game development)_", - R"_(Libraries used for game development)_", - R"_(generics)_", - R"_(Generics)_", - R"_(Tooling to work with Haskell's Generics)_", - R"_(prelude)_", - R"_(Prelude)_", - R"_(Libraries that provide default imports)_", - R"_(language)_", - R"_(Language)_", - R"_(Tooling for interfacing with other programming languages from Haskell programs)_", - R"_(natural-language)_", - R"_(Natural Language)_", - R"_(Tooling to working with natural languages)_", - R"_(network)_", - R"_(Network Development)_", - R"_(Connection pools, DNS, HTTP, API clients and network protocols)_", - R"_(maths)_", - R"_(Mathematics)_", - R"_(Numerical and Mathematical packages)_", - R"_(parser-implementations)_", - R"_(Parser Implementations)_", - R"_(Parsing data formats)_", - R"_(parsers)_", - R"_(Parsers)_", - R"_(Libraries to ingest and parse data)_", - R"_(parsing)_", - R"_(Parsing)_", - R"_(Parser generators, combinators and tools to help with parsing)_", - R"_(system)_", - R"_(System)_", - R"_(Programming and communicating with the Operating System)_", - R"_(testing)_", - R"_(Testing)_", - R"_(Test frameworks)_", - R"_(text)_", - R"_(Text)_", - R"_(Working with textual data and algorithms)_", - R"_(web)_", - R"_(Web Development)_", - R"_(Programming for the web)_", - R"_(xml)_", - R"_(XML)_", - R"_(Libraries to consume and produce XML documents)_", - R"_(Algorithm)_", - R"_(Crypto)_", - R"_(CLI)_", - R"_(CLI & TUI Development)_", - R"_(TUI)_", - R"_(Command Line)_", - R"_(CommandLine)_", - R"_(Numeric)_", - R"_(Numerical)_", - R"_(Numerics)_", - R"_(Arithmetic)_", - R"_(Number Theory)_", - R"_(Math)_", - R"_(Maths)_", - R"_(Algebra)_", - R"_(Parser Builder)_", - R"_(Parser Combinators)_", - R"_(Parser)_", - R"_(ParserCombinators)_", - R"_(Network)_", - R"_(Data Network)_", - R"_(Network APIs)_", - R"_(Network Control)_", - R"_(NetworkAPI)_", - R"_(NetworkAPIs)_", - R"_(Networking)_", -};// -- initialize record table -- -SpecializedRecordTable<0> recordTable{}; -// -- Table: flora_category -Own rel_1_flora_category = mk(); -souffle::RelationWrapper wrapper_rel_1_flora_category; -// -- Table: normalise_category -Own rel_2_normalise_category = mk(); -souffle::RelationWrapper wrapper_rel_2_normalise_category; -// -- Table: user_package_category -Own rel_3_user_package_category = mk(); -souffle::RelationWrapper wrapper_rel_3_user_package_category; -// -- Table: normalise_issue -Own rel_4_normalise_issue = mk(); -souffle::RelationWrapper wrapper_rel_4_normalise_issue; -// -- Table: normalised_package_category -Own rel_5_normalised_package_category = mk(); -souffle::RelationWrapper wrapper_rel_5_normalised_package_category; -public: -Sf_categorise() -: wrapper_rel_1_flora_category(0, *rel_1_flora_category, *this, "flora_category", std::array{{"s:symbol","s:symbol","s:symbol"}}, std::array{{"slug","name","synopsis"}}, 0) -, wrapper_rel_2_normalise_category(1, *rel_2_normalise_category, *this, "normalise_category", std::array{{"s:symbol","s:symbol"}}, std::array{{"user_input","normalised"}}, 0) -, wrapper_rel_3_user_package_category(2, *rel_3_user_package_category, *this, "user_package_category", std::array{{"s:symbol"}}, std::array{{"category"}}, 0) -, wrapper_rel_4_normalise_issue(3, *rel_4_normalise_issue, *this, "normalise_issue", std::array{{"s:symbol"}}, std::array{{"category"}}, 0) -, wrapper_rel_5_normalised_package_category(4, *rel_5_normalised_package_category, *this, "normalised_package_category", std::array{{"s:symbol"}}, std::array{{"category"}}, 0) -{ -addRelation("flora_category", wrapper_rel_1_flora_category, false, true); -addRelation("normalise_category", wrapper_rel_2_normalise_category, false, false); -addRelation("user_package_category", wrapper_rel_3_user_package_category, true, false); -addRelation("normalise_issue", wrapper_rel_4_normalise_issue, false, true); -addRelation("normalised_package_category", wrapper_rel_5_normalised_package_category, false, true); -} -~Sf_categorise() { -} - -private: -std::string inputDirectory; -std::string outputDirectory; -SignalHandler* signalHandler {SignalHandler::instance()}; -std::atomic ctr {}; -std::atomic iter {}; - -void runFunction(std::string inputDirectoryArg, - std::string outputDirectoryArg, - bool performIOArg, - bool pruneImdtRelsArg) { - this->inputDirectory = std::move(inputDirectoryArg); - this->outputDirectory = std::move(outputDirectoryArg); - this->performIO = performIOArg; - this->pruneImdtRels = pruneImdtRelsArg; - - // set default threads (in embedded mode) - // if this is not set, and omp is used, the default omp setting of number of cores is used. -#if defined(_OPENMP) - if (0 < getNumThreads()) { omp_set_num_threads(getNumThreads()); } -#endif - - signalHandler->set(); -// -- query evaluation -- -{ - std::vector args, ret; -subroutine_0(args, ret); -} -{ - std::vector args, ret; -subroutine_1(args, ret); -} -{ - std::vector args, ret; -subroutine_2(args, ret); -} -{ - std::vector args, ret; -subroutine_3(args, ret); -} -{ - std::vector args, ret; -subroutine_4(args, ret); -} - -// -- relation hint statistics -- -signalHandler->reset(); -} -public: -void run() override { runFunction("", "", false, false); } -public: -void runAll(std::string inputDirectoryArg = "", std::string outputDirectoryArg = "", bool performIOArg=true, bool pruneImdtRelsArg=true) override { runFunction(inputDirectoryArg, outputDirectoryArg, performIOArg, pruneImdtRelsArg); -} -public: -void printAll(std::string outputDirectoryArg = "") override { -try {std::map directiveMap({{"IO","file"},{"attributeNames","category"},{"auxArity","0"},{"name","normalised_package_category"},{"operation","output"},{"output-dir","."},{"params","{\"records\": {}, \"relation\": {\"arity\": 1, \"params\": [\"category\"]}}"},{"types","{\"ADTs\": {}, \"records\": {}, \"relation\": {\"arity\": 1, \"types\": [\"s:symbol\"]}}"}}); -if (!outputDirectoryArg.empty()) {directiveMap["output-dir"] = outputDirectoryArg;} -IOSystem::getInstance().getWriter(directiveMap, symTable, recordTable)->writeAll(*rel_5_normalised_package_category); -} catch (std::exception& e) {std::cerr << e.what();exit(1);} -try {std::map directiveMap({{"IO","file"},{"attributeNames","slug\tname\tsynopsis"},{"auxArity","0"},{"name","flora_category"},{"operation","output"},{"output-dir","."},{"params","{\"records\": {}, \"relation\": {\"arity\": 3, \"params\": [\"slug\", \"name\", \"synopsis\"]}}"},{"types","{\"ADTs\": {}, \"records\": {}, \"relation\": {\"arity\": 3, \"types\": [\"s:symbol\", \"s:symbol\", \"s:symbol\"]}}"}}); -if (!outputDirectoryArg.empty()) {directiveMap["output-dir"] = outputDirectoryArg;} -IOSystem::getInstance().getWriter(directiveMap, symTable, recordTable)->writeAll(*rel_1_flora_category); -} catch (std::exception& e) {std::cerr << e.what();exit(1);} -try {std::map directiveMap({{"IO","file"},{"attributeNames","category"},{"auxArity","0"},{"name","normalise_issue"},{"operation","output"},{"output-dir","."},{"params","{\"records\": {}, \"relation\": {\"arity\": 1, \"params\": [\"category\"]}}"},{"types","{\"ADTs\": {}, \"records\": {}, \"relation\": {\"arity\": 1, \"types\": [\"s:symbol\"]}}"}}); -if (!outputDirectoryArg.empty()) {directiveMap["output-dir"] = outputDirectoryArg;} -IOSystem::getInstance().getWriter(directiveMap, symTable, recordTable)->writeAll(*rel_4_normalise_issue); -} catch (std::exception& e) {std::cerr << e.what();exit(1);} -} -public: -void loadAll(std::string inputDirectoryArg = "") override { -try {std::map directiveMap({{"IO","file"},{"attributeNames","category"},{"auxArity","0"},{"fact-dir","."},{"name","user_package_category"},{"operation","input"},{"params","{\"records\": {}, \"relation\": {\"arity\": 1, \"params\": [\"category\"]}}"},{"types","{\"ADTs\": {}, \"records\": {}, \"relation\": {\"arity\": 1, \"types\": [\"s:symbol\"]}}"}}); -if (!inputDirectoryArg.empty()) {directiveMap["fact-dir"] = inputDirectoryArg;} -IOSystem::getInstance().getReader(directiveMap, symTable, recordTable)->readAll(*rel_3_user_package_category); -} catch (std::exception& e) {std::cerr << "Error loading user_package_category data: " << e.what() << '\n';} -} -public: -void dumpInputs() override { -try {std::map rwOperation; -rwOperation["IO"] = "stdout"; -rwOperation["name"] = "user_package_category"; -rwOperation["types"] = "{\"relation\": {\"arity\": 1, \"auxArity\": 0, \"types\": [\"s:symbol\"]}}"; -IOSystem::getInstance().getWriter(rwOperation, symTable, recordTable)->writeAll(*rel_3_user_package_category); -} catch (std::exception& e) {std::cerr << e.what();exit(1);} -} -public: -void dumpOutputs() override { -try {std::map rwOperation; -rwOperation["IO"] = "stdout"; -rwOperation["name"] = "normalised_package_category"; -rwOperation["types"] = "{\"relation\": {\"arity\": 1, \"auxArity\": 0, \"types\": [\"s:symbol\"]}}"; -IOSystem::getInstance().getWriter(rwOperation, symTable, recordTable)->writeAll(*rel_5_normalised_package_category); -} catch (std::exception& e) {std::cerr << e.what();exit(1);} -try {std::map rwOperation; -rwOperation["IO"] = "stdout"; -rwOperation["name"] = "flora_category"; -rwOperation["types"] = "{\"relation\": {\"arity\": 3, \"auxArity\": 0, \"types\": [\"s:symbol\", \"s:symbol\", \"s:symbol\"]}}"; -IOSystem::getInstance().getWriter(rwOperation, symTable, recordTable)->writeAll(*rel_1_flora_category); -} catch (std::exception& e) {std::cerr << e.what();exit(1);} -try {std::map rwOperation; -rwOperation["IO"] = "stdout"; -rwOperation["name"] = "normalise_issue"; -rwOperation["types"] = "{\"relation\": {\"arity\": 1, \"auxArity\": 0, \"types\": [\"s:symbol\"]}}"; -IOSystem::getInstance().getWriter(rwOperation, symTable, recordTable)->writeAll(*rel_4_normalise_issue); -} catch (std::exception& e) {std::cerr << e.what();exit(1);} -} -public: -SymbolTable& getSymbolTable() override { -return symTable; -} -RecordTable& getRecordTable() override { -return recordTable; -} -void setNumThreads(std::size_t numThreadsValue) override { -SouffleProgram::setNumThreads(numThreadsValue); -symTable.setNumLanes(getNumThreads()); -recordTable.setNumLanes(getNumThreads()); -} -void executeSubroutine(std::string name, const std::vector& args, std::vector& ret) override { -if (name == "stratum_0") { -subroutine_0(args, ret); -return;} -if (name == "stratum_1") { -subroutine_1(args, ret); -return;} -if (name == "stratum_2") { -subroutine_2(args, ret); -return;} -if (name == "stratum_3") { -subroutine_3(args, ret); -return;} -if (name == "stratum_4") { -subroutine_4(args, ret); -return;} -fatal("unknown subroutine"); -} -#ifdef _MSC_VER -#pragma warning(disable: 4100) -#endif // _MSC_VER -void subroutine_0(const std::vector& args, std::vector& ret) { -signalHandler->setMsg(R"_(flora_category("algorithms","Algorithms","Algorithms implemented in Haskell, like sorting, searching"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [23:1-23:106])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(0)),ramBitCast(RamSigned(1)),ramBitCast(RamSigned(2))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("bioinformatics","Bioinformatics","Tooling, algorithms and techniques for the domain of Bioinformatics"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [24:1-24:123])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(3)),ramBitCast(RamSigned(4)),ramBitCast(RamSigned(5))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("command-line","CLI & TUI tooling","Libraries to develope command-line interfaces"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [25:1-25:102])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(6)),ramBitCast(RamSigned(7)),ramBitCast(RamSigned(8))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("compilers-interpreters","Compilers and Interpreters","Tooling to create compilers and interpreters"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [26:1-26:120])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(9)),ramBitCast(RamSigned(10)),ramBitCast(RamSigned(11))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("cryptography","Cryptography","Algorithms for encrypting and hashing data"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [27:1-27:94])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(12)),ramBitCast(RamSigned(13)),ramBitCast(RamSigned(14))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("data-structures","Data Structures","Data structures, whether they are purely functional or mutable, there's always one for you"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [28:1-28:148])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(15)),ramBitCast(RamSigned(16)),ramBitCast(RamSigned(17))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("databases","Databases","Database drivers and interfaces"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [29:1-29:77])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(18)),ramBitCast(RamSigned(19)),ramBitCast(RamSigned(20))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("development","Development","Development helpers, integration with other languages"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [30:1-30:103])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(21)),ramBitCast(RamSigned(22)),ramBitCast(RamSigned(23))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("distributed","Distributed Systems & Computation","Tooling and techniques for writing distributed systems"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [31:1-31:126])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(24)),ramBitCast(RamSigned(25)),ramBitCast(RamSigned(26))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("ffi","FFI","Tooling to work with the Foreign Function Interface"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [32:1-32:85])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(27)),ramBitCast(RamSigned(28)),ramBitCast(RamSigned(29))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("game-dev","Game development","Libraries used for game development"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [33:1-33:87])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(30)),ramBitCast(RamSigned(31)),ramBitCast(RamSigned(32))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("generics","Generics","Tooling to work with Haskell's Generics"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [34:1-34:83])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(33)),ramBitCast(RamSigned(34)),ramBitCast(RamSigned(35))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("prelude","Prelude","Libraries that provide default imports"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [35:1-35:80])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(36)),ramBitCast(RamSigned(37)),ramBitCast(RamSigned(38))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("language","Language","Tooling for interfacing with other programming languages from Haskell programs"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [36:1-36:122])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(39)),ramBitCast(RamSigned(40)),ramBitCast(RamSigned(41))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("natural-language","Natural Language","Tooling to working with natural languages"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [37:1-37:101])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(42)),ramBitCast(RamSigned(43)),ramBitCast(RamSigned(44))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("network","Network Development","Connection pools, DNS, HTTP, API clients and network protocols"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [38:1-38:116])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(45)),ramBitCast(RamSigned(46)),ramBitCast(RamSigned(47))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("maths","Mathematics","Numerical and Mathematical packages"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [39:1-39:79])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(48)),ramBitCast(RamSigned(49)),ramBitCast(RamSigned(50))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("parser-implementations","Parser Implementations","Parsing data formats"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [40:1-40:92])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(51)),ramBitCast(RamSigned(52)),ramBitCast(RamSigned(53))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("parsers","Parsers","Libraries to ingest and parse data"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [41:1-41:75])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(54)),ramBitCast(RamSigned(55)),ramBitCast(RamSigned(56))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("parsing","Parsing","Parser generators, combinators and tools to help with parsing"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [42:1-42:103])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(57)),ramBitCast(RamSigned(58)),ramBitCast(RamSigned(59))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("system","System","Programming and communicating with the Operating System"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [43:1-43:95])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(60)),ramBitCast(RamSigned(61)),ramBitCast(RamSigned(62))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("testing","Testing","Test frameworks"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [44:1-44:57])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(63)),ramBitCast(RamSigned(64)),ramBitCast(RamSigned(65))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("text","Text","Working with textual data and algorithms"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [45:1-45:76])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(66)),ramBitCast(RamSigned(67)),ramBitCast(RamSigned(68))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("web","Web Development","Programming for the web"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [46:1-46:69])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(69)),ramBitCast(RamSigned(70)),ramBitCast(RamSigned(71))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(flora_category("xml","XML","Libraries to consume and produce XML documents"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [47:1-47:80])_"); -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(72)),ramBitCast(RamSigned(73)),ramBitCast(RamSigned(74))}}; -rel_1_flora_category->insert(tuple,READ_OP_CONTEXT(rel_1_flora_category_op_ctxt)); -} -();if (performIO) { -try {std::map directiveMap({{"IO","file"},{"attributeNames","slug\tname\tsynopsis"},{"auxArity","0"},{"name","flora_category"},{"operation","output"},{"output-dir","."},{"params","{\"records\": {}, \"relation\": {\"arity\": 3, \"params\": [\"slug\", \"name\", \"synopsis\"]}}"},{"types","{\"ADTs\": {}, \"records\": {}, \"relation\": {\"arity\": 3, \"types\": [\"s:symbol\", \"s:symbol\", \"s:symbol\"]}}"}}); -if (!outputDirectory.empty()) {directiveMap["output-dir"] = outputDirectory;} -IOSystem::getInstance().getWriter(directiveMap, symTable, recordTable)->writeAll(*rel_1_flora_category); -} catch (std::exception& e) {std::cerr << e.what();exit(1);} -} -} -#ifdef _MSC_VER -#pragma warning(default: 4100) -#endif // _MSC_VER -#ifdef _MSC_VER -#pragma warning(disable: 4100) -#endif // _MSC_VER -void subroutine_1(const std::vector& args, std::vector& ret) { -signalHandler->setMsg(R"_(normalise_category(X,X) :- - flora_category(_,X,_). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [52:1-52:53])_"); -if(!(rel_1_flora_category->empty())) { -[&](){ -CREATE_OP_CONTEXT(rel_1_flora_category_op_ctxt,rel_1_flora_category->createContext()); -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -for(const auto& env0 : *rel_1_flora_category) { -Tuple tuple{{ramBitCast(env0[1]),ramBitCast(env0[1])}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -} -();} -signalHandler->setMsg(R"_(normalise_category("Algorithm","Algorithms"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [53:1-53:47])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(75)),ramBitCast(RamSigned(1))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Crypto","Cryptography"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [55:1-55:46])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(76)),ramBitCast(RamSigned(13))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("CLI","CLI & TUI Development"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [57:1-57:52])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(77)),ramBitCast(RamSigned(78))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("TUI","CLI & TUI Development"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [58:1-58:52])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(79)),ramBitCast(RamSigned(78))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Command Line","CLI & TUI Development"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [60:1-60:61])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(80)),ramBitCast(RamSigned(78))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("CommandLine","CLI & TUI Development"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [61:1-61:60])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(81)),ramBitCast(RamSigned(78))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Numeric","Mathematics"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [63:1-63:46])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(82)),ramBitCast(RamSigned(49))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Numerical","Mathematics"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [64:1-64:48])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(83)),ramBitCast(RamSigned(49))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Numerics","Mathematics"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [65:1-65:47])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(84)),ramBitCast(RamSigned(49))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Arithmetic","Mathematics"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [66:1-66:49])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(85)),ramBitCast(RamSigned(49))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Number Theory","Mathematics"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [67:1-67:52])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(86)),ramBitCast(RamSigned(49))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Math","Mathematics"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [68:1-68:43])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(87)),ramBitCast(RamSigned(49))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Mathematics","Mathematics"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [69:1-69:50])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(49)),ramBitCast(RamSigned(49))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Maths","Mathematics"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [70:1-70:44])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(88)),ramBitCast(RamSigned(49))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Algebra","Mathematics"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [71:1-71:46])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(89)),ramBitCast(RamSigned(49))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Parser Builder","Parsers"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [73:1-73:49])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(90)),ramBitCast(RamSigned(55))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Parser Combinators","Parsers"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [74:1-74:53])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(91)),ramBitCast(RamSigned(55))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Parser","Parsers"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [75:1-75:41])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(92)),ramBitCast(RamSigned(55))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("ParserCombinators","Parsers"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [76:1-76:52])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(93)),ramBitCast(RamSigned(55))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Parsers","Parsers"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [77:1-77:42])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(55)),ramBitCast(RamSigned(55))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Parsing","Parsers"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [78:1-78:42])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(58)),ramBitCast(RamSigned(55))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Network","Network Development"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [80:1-80:54])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(94)),ramBitCast(RamSigned(46))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Data Network","Network Development"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [81:1-81:59])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(95)),ramBitCast(RamSigned(46))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Network APIs","Network Development"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [82:1-82:59])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(96)),ramBitCast(RamSigned(46))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Network Control","Network Development"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [83:1-83:62])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(97)),ramBitCast(RamSigned(46))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("NetworkAPI","Network Development"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [84:1-84:57])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(98)),ramBitCast(RamSigned(46))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("NetworkAPIs","Network Development"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [85:1-85:58])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(99)),ramBitCast(RamSigned(46))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();signalHandler->setMsg(R"_(normalise_category("Networking","Network Development"). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [86:1-86:57])_"); -[&](){ -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -Tuple tuple{{ramBitCast(RamSigned(100)),ramBitCast(RamSigned(46))}}; -rel_2_normalise_category->insert(tuple,READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -} -();if (pruneImdtRels) rel_1_flora_category->purge(); -} -#ifdef _MSC_VER -#pragma warning(default: 4100) -#endif // _MSC_VER -#ifdef _MSC_VER -#pragma warning(disable: 4100) -#endif // _MSC_VER -void subroutine_2(const std::vector& args, std::vector& ret) { -if (performIO) { -try {std::map directiveMap({{"IO","file"},{"attributeNames","category"},{"auxArity","0"},{"fact-dir","."},{"name","user_package_category"},{"operation","input"},{"params","{\"records\": {}, \"relation\": {\"arity\": 1, \"params\": [\"category\"]}}"},{"types","{\"ADTs\": {}, \"records\": {}, \"relation\": {\"arity\": 1, \"types\": [\"s:symbol\"]}}"}}); -if (!inputDirectory.empty()) {directiveMap["fact-dir"] = inputDirectory;} -IOSystem::getInstance().getReader(directiveMap, symTable, recordTable)->readAll(*rel_3_user_package_category); -} catch (std::exception& e) {std::cerr << "Error loading user_package_category data: " << e.what() << '\n';} -} -} -#ifdef _MSC_VER -#pragma warning(default: 4100) -#endif // _MSC_VER -#ifdef _MSC_VER -#pragma warning(disable: 4100) -#endif // _MSC_VER -void subroutine_3(const std::vector& args, std::vector& ret) { -signalHandler->setMsg(R"_(normalise_issue(category) :- - user_package_category(category), - !normalise_category(category,_). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [16:1-18:36])_"); -if(!(rel_3_user_package_category->empty())) { -[&](){ -CREATE_OP_CONTEXT(rel_4_normalise_issue_op_ctxt,rel_4_normalise_issue->createContext()); -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -CREATE_OP_CONTEXT(rel_3_user_package_category_op_ctxt,rel_3_user_package_category->createContext()); -for(const auto& env0 : *rel_3_user_package_category) { -if( !(!rel_2_normalise_category->lowerUpperRange_10(Tuple{{ramBitCast(env0[0]), ramBitCast(MIN_RAM_SIGNED)}},Tuple{{ramBitCast(env0[0]), ramBitCast(MAX_RAM_SIGNED)}},READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)).empty())) { -Tuple tuple{{ramBitCast(env0[0])}}; -rel_4_normalise_issue->insert(tuple,READ_OP_CONTEXT(rel_4_normalise_issue_op_ctxt)); -} -} -} -();} -if (performIO) { -try {std::map directiveMap({{"IO","file"},{"attributeNames","category"},{"auxArity","0"},{"name","normalise_issue"},{"operation","output"},{"output-dir","."},{"params","{\"records\": {}, \"relation\": {\"arity\": 1, \"params\": [\"category\"]}}"},{"types","{\"ADTs\": {}, \"records\": {}, \"relation\": {\"arity\": 1, \"types\": [\"s:symbol\"]}}"}}); -if (!outputDirectory.empty()) {directiveMap["output-dir"] = outputDirectory;} -IOSystem::getInstance().getWriter(directiveMap, symTable, recordTable)->writeAll(*rel_4_normalise_issue); -} catch (std::exception& e) {std::cerr << e.what();exit(1);} -} -} -#ifdef _MSC_VER -#pragma warning(default: 4100) -#endif // _MSC_VER -#ifdef _MSC_VER -#pragma warning(disable: 4100) -#endif // _MSC_VER -void subroutine_4(const std::vector& args, std::vector& ret) { -signalHandler->setMsg(R"_(normalised_package_category(normalised) :- - user_package_category(user_category), - normalise_category(user_category,normalised). -in file /home/hecate/Projects/Flora/2-flora-server/cbits/categorise.dl [10:1-12:49])_"); -if(!(rel_3_user_package_category->empty()) && !(rel_2_normalise_category->empty())) { -[&](){ -CREATE_OP_CONTEXT(rel_5_normalised_package_category_op_ctxt,rel_5_normalised_package_category->createContext()); -CREATE_OP_CONTEXT(rel_2_normalise_category_op_ctxt,rel_2_normalise_category->createContext()); -CREATE_OP_CONTEXT(rel_3_user_package_category_op_ctxt,rel_3_user_package_category->createContext()); -for(const auto& env0 : *rel_3_user_package_category) { -auto range = rel_2_normalise_category->lowerUpperRange_10(Tuple{{ramBitCast(env0[0]), ramBitCast(MIN_RAM_SIGNED)}},Tuple{{ramBitCast(env0[0]), ramBitCast(MAX_RAM_SIGNED)}},READ_OP_CONTEXT(rel_2_normalise_category_op_ctxt)); -for(const auto& env1 : range) { -Tuple tuple{{ramBitCast(env1[1])}}; -rel_5_normalised_package_category->insert(tuple,READ_OP_CONTEXT(rel_5_normalised_package_category_op_ctxt)); -} -} -} -();} -if (performIO) { -try {std::map directiveMap({{"IO","file"},{"attributeNames","category"},{"auxArity","0"},{"name","normalised_package_category"},{"operation","output"},{"output-dir","."},{"params","{\"records\": {}, \"relation\": {\"arity\": 1, \"params\": [\"category\"]}}"},{"types","{\"ADTs\": {}, \"records\": {}, \"relation\": {\"arity\": 1, \"types\": [\"s:symbol\"]}}"}}); -if (!outputDirectory.empty()) {directiveMap["output-dir"] = outputDirectory;} -IOSystem::getInstance().getWriter(directiveMap, symTable, recordTable)->writeAll(*rel_5_normalised_package_category); -} catch (std::exception& e) {std::cerr << e.what();exit(1);} -} -if (pruneImdtRels) rel_3_user_package_category->purge(); -if (pruneImdtRels) rel_2_normalise_category->purge(); -} -#ifdef _MSC_VER -#pragma warning(default: 4100) -#endif // _MSC_VER -}; -SouffleProgram *newInstance_categorise(){return new Sf_categorise;} -SymbolTable *getST_categorise(SouffleProgram *p){return &reinterpret_cast(p)->getSymbolTable();} - -#ifdef __EMBEDDED_SOUFFLE__ -class factory_Sf_categorise: public souffle::ProgramFactory { -SouffleProgram *newInstance() { -return new Sf_categorise(); -}; -public: -factory_Sf_categorise() : ProgramFactory("categorise"){} -}; -extern "C" { -factory_Sf_categorise __factory_Sf_categorise_instance; -} -} -#else -} -int main(int argc, char** argv) -{ -try{ -souffle::CmdOptions opt(R"(categorise.dl)", -R"()", -R"()", -false, -R"()", -1); -if (!opt.parse(argc,argv)) return 1; -souffle::Sf_categorise obj; -#if defined(_OPENMP) -obj.setNumThreads(opt.getNumJobs()); - -#endif -obj.runAll(opt.getInputFileDir(), opt.getOutputFileDir()); -return 0; -} catch(std::exception &e) { souffle::SignalHandler::instance()->error(e.what());} -} - -#endif diff --git a/environment.sh b/environment.sh index f0d5f0e0..bf396a26 100755 --- a/environment.sh +++ b/environment.sh @@ -17,6 +17,9 @@ export FLORA_HTTP_PORT=8083 export FLORA_ENVIRONMENT="development" export FLORA_DOMAIN="localhost" +# Either "stdout" or "json" +export FLORA_LOGGING_DESTINATION="stdout" + # Compatibility mode for Hackage. # This includes: # diff --git a/flora.cabal b/flora.cabal index 597bcb29..fd038fee 100644 --- a/flora.cabal +++ b/flora.cabal @@ -58,7 +58,9 @@ common common-ghc-options -Wall -Wcompat -Werror -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints -fhide-source-paths -Wno-unused-do-bind -fshow-hole-constraints - -flate-specialise + + if flag(release-build) + ghc-options: -flate-specialise -funbox-strict-fields common common-rts-options ghc-options: -rtsopts -threaded -with-rtsopts "-N -T" @@ -115,12 +117,13 @@ library FloraWeb.Server FloraWeb.Server.Auth FloraWeb.Server.Auth.Types - FloraWeb.Server.Logging.Metrics - FloraWeb.Server.Logging.Tracing + FloraWeb.Server.Logging + FloraWeb.Server.Metrics FloraWeb.Server.Pages FloraWeb.Server.Pages.Admin FloraWeb.Server.Pages.Packages FloraWeb.Server.Pages.Sessions + FloraWeb.Server.Tracing FloraWeb.Server.Util FloraWeb.Session FloraWeb.Templates @@ -190,7 +193,7 @@ library , uuid ^>=1.3 , vector ^>=0.12 , wai ^>=3.2 - , wai-logger ^>=2.3 + , wai-log ^>=0.3 , wai-middleware-heartbeat ^>=0.0 , wai-middleware-prometheus ^>=1.0 , warp ^>=3.3 @@ -282,3 +285,10 @@ test-suite flora-test Flora.PackageSpec Flora.TestUtils Flora.UserSpec + +flag release-build + description: + Compile the project with additional optimisations (takes longer) + + default: False + manual: True diff --git a/src/Flora/Environment.hs b/src/Flora/Environment.hs index 599e46c2..a34e388a 100644 --- a/src/Flora/Environment.hs +++ b/src/Flora/Environment.hs @@ -1,8 +1,9 @@ {-# OPTIONS_GHC -Wno-orphans #-} module Flora.Environment ( FloraEnv(..) - , LoggingEnv(..) , DeploymentEnv(..) + , LoggingEnv(..) + , LoggingDestination(..) , TestEnv(..) , getFloraEnv , getFloraTestEnv @@ -14,6 +15,7 @@ import Data.Bifunctor import Data.Pool (Pool, createPool) import Data.Text import qualified Data.Text as T +import Data.Text.Display (Display (..)) import Data.Time (NominalDiffTime) import Data.Word (Word16) import Database.PostgreSQL.Entity.DBT @@ -37,12 +39,23 @@ data FloraEnv = FloraEnv data DeploymentEnv = Production | Development - | Tests + | Test deriving stock (Show, Eq, Generic, Enum, Bounded) +instance Display DeploymentEnv where + displayBuilder Production = "prod" + displayBuilder Development = "dev" + displayBuilder Test = "test" + +data LoggingDestination + = StdOut -- ^ Logs are printed on the standard output + | Json -- ^ Logs are printed on the standard output in JSON format + deriving (Show, Generic) + data LoggingEnv = LoggingEnv { sentryDSN :: Maybe String , prometheusEnabled :: Bool + , logger :: LoggingDestination } deriving stock (Show, Generic) @@ -127,6 +140,7 @@ parseLoggingEnv = LoggingEnv <$> var (pure . Just <=< nonempty) "FLORA_SENTRY_DSN" (help "Sentry DSN" <> def Nothing) <*> switch "FLORA_PROMETHEUS_ENABLED" (help "Whether or not Prometheus is enabled") + <*> var loggingDestination "FLORA_LOGGING_DESTINATION" (help "Where do the logs go") parsePort :: Parser Error Word16 parsePort = var port "FLORA_HTTP_PORT" (help "HTTP Port for Flora") @@ -186,4 +200,10 @@ timeout t = second fromIntegral (int >=> nonNegative $ t) deploymentEnv :: Reader Error DeploymentEnv deploymentEnv "production" = Right Production deploymentEnv "development" = Right Development +deploymentEnv "test" = Right Test deploymentEnv e = Left $ unread e + +loggingDestination :: Reader Error LoggingDestination +loggingDestination "stdout" = Right StdOut +loggingDestination "json" = Right Json +loggingDestination e = Left $ unread e diff --git a/src/Flora/Model/UserSession.hs b/src/Flora/Model/UserSession.hs deleted file mode 100644 index 3643f1d6..00000000 --- a/src/Flora/Model/UserSession.hs +++ /dev/null @@ -1,52 +0,0 @@ -module Flora.Model.UserSession where - -import Data.Map.Strict (Map) -import qualified Data.Map.Strict as Map -import Data.Text -import Data.Time -import Data.UUID -import qualified Data.UUID.V4 as UUID -import Database.PostgreSQL.Entity -import Database.PostgreSQL.Entity.Types -import Database.PostgreSQL.Simple -import Database.PostgreSQL.Simple.FromField -import Database.PostgreSQL.Simple.Newtypes -import Database.PostgreSQL.Simple.ToField -import Database.PostgreSQL.Transact -import Env.Generic -import Flora.Model.User (UserId) - -newtype UserSessionId = UserSessionId { getUserSessionId :: UUID } - deriving (Show, Eq, FromField, ToField) - via UUID - -data UserSession = UserSession - { userSessionId :: UserSessionId - , sessionId :: Text - , userId :: UserId - , sessionData :: SessionData - , createdAt :: UTCTime - } deriving stock (Show, Eq, Generic) - deriving anyclass (FromRow, ToRow) - deriving Entity - via (GenericEntity '[TableName "user_sessions"] UserSession) - -newtype SessionData = SessionData {getSessionData :: Map Text Text} - deriving stock (Show, Eq, Generic) - deriving (FromField, ToField) - via Aeson (Map Text Text) - -newSessionId :: IO UserSessionId -newSessionId = UserSessionId <$> UUID.nextRandom - -insertSession :: UserSession -> DBT IO () -insertSession = insert @UserSession - -deleteSession :: UserSessionId -> DBT IO () -deleteSession sessionId = delete @UserSession (Only sessionId) - -getUserSession :: UserSessionId -> DBT IO (Maybe UserSession) -getUserSession sessionId = selectById @UserSession (Only sessionId) - -lookup :: Text -> SessionData -> Maybe Text -lookup key (SessionData sdMap) = Map.lookup key sdMap diff --git a/src/FloraWeb/Server.hs b/src/FloraWeb/Server.hs index 883b3798..7123d6ae 100644 --- a/src/FloraWeb/Server.hs +++ b/src/FloraWeb/Server.hs @@ -6,8 +6,8 @@ import Control.Monad.Reader (MonadIO (liftIO), ReaderT (runReaderT), withReaderT) import Data.Maybe (isJust) import Data.Text.Display (display) -import Network.Wai.Handler.Warp (defaultSettings, runSettings, setLogger, - setOnException, setPort) +import Network.Wai.Handler.Warp (defaultSettings, runSettings, setOnException, + setPort) import Network.Wai.Middleware.Heartbeat (heartbeatMiddleware) import Optics.Core import qualified Prometheus @@ -24,14 +24,14 @@ import Flora.Environment (FloraEnv (..), LoggingEnv (..), getFloraEnv) import FloraWeb.Routes import qualified FloraWeb.Routes.Pages as Pages import FloraWeb.Server.Auth (FloraAuthContext, authHandler) -import FloraWeb.Server.Logging.Metrics -import FloraWeb.Server.Logging.Tracing +import qualified FloraWeb.Server.Logging as Logging +import FloraWeb.Server.Metrics import qualified FloraWeb.Server.Pages as Pages +import FloraWeb.Server.Tracing import FloraWeb.Types import Log (Logger, defaultLogLevel) import qualified Log -import qualified Log.Backend.StandardOutput as Log -import qualified Network.Wai.Logger as WAI +import qualified Network.Wai.Log as WaiLog runFlora :: IO () runFlora = bracket getFloraEnv shutdownFlora $ \env -> do @@ -42,25 +42,28 @@ runFlora = bracket getFloraEnv shutdownFlora $ \env -> do blueMessage $ "📋 Service Prometheus metrics on " <> baseURL <> "/metrics" Prometheus.register ghcMetrics void $ Prometheus.register procMetrics - runServer env + let withLogger = Logging.makeLogger (env ^. #logging ^. #logger) + withLogger $ \appLogger -> + runServer appLogger env shutdownFlora :: FloraEnv -> IO () shutdownFlora env = do Pool.destroyAllResources (env ^. #pool) -runServer :: FloraEnv -> IO () -runServer floraEnv = WAI.withStdoutLogger $ \waiLogger -> Log.withStdOutLogger $ \appLogger -> do +runServer :: Logger -> FloraEnv -> IO () +runServer appLogger floraEnv = do + loggingMiddleware <- Logging.runLog floraEnv appLogger WaiLog.mkLogMiddleware let webEnv = WebEnv floraEnv webEnvStore <- liftIO $ newWebEnvStore webEnv let server = mkServer appLogger webEnvStore floraEnv let warpSettings = setPort (fromIntegral $ httpPort floraEnv) $ - setLogger waiLogger $ setOnException (sentryOnException (floraEnv ^. #environment) (floraEnv ^. #logging)) defaultSettings runSettings warpSettings $ prometheusMiddleware (floraEnv ^. #environment) (floraEnv ^. #logging) . heartbeatMiddleware + . loggingMiddleware . const $ server mkServer :: Logger -> WebEnvStore -> FloraEnv -> Application diff --git a/src/FloraWeb/Server/Auth.hs b/src/FloraWeb/Server/Auth.hs index aed602b2..710d2437 100644 --- a/src/FloraWeb/Server/Auth.hs +++ b/src/FloraWeb/Server/Auth.hs @@ -22,22 +22,23 @@ import Flora.Model.PersistentSession import Flora.Model.User import Flora.Model.User.Query import FloraWeb.Server.Auth.Types +import qualified FloraWeb.Server.Logging as Logging import FloraWeb.Session import FloraWeb.Types -import Log (LogT, Logger, defaultLogLevel, runLogT) +import Log (LogT, Logger) import Network.HTTP.Types (hCookie) type FloraAuthContext = AuthHandler Request (Headers '[Header "Set-Cookie" SetCookie] (Session 'Visitor)) authHandler :: Logger -> FloraEnv -> FloraAuthContext -authHandler logger floraEnv = mkAuthHandler (\request -> runLogT "flora-auth" logger defaultLogLevel (handler request)) +authHandler logger floraEnv = mkAuthHandler (\request -> Logging.runLog floraEnv logger (handler request)) where pool = floraEnv ^. #pool handler :: Request -> LogT Handler (Headers '[Header "Set-Cookie" SetCookie] (Session 'Visitor)) handler req = do let cookies = getCookies req mbPersistentSessionId <- lift $ getSessionId cookies - mbPersistentSession <- lift $ getInTheFuckingSessionShinji pool mbPersistentSessionId + mbPersistentSession <- getInTheFuckingSessionShinji pool mbPersistentSessionId mUserInfo <- lift $ fetchUser pool mbPersistentSession (mUser, sessionId) <- do case mUserInfo of @@ -50,7 +51,6 @@ authHandler logger floraEnv = mkAuthHandler (\request -> runLogT "flora-auth" lo let sessionCookie = craftSessionCookie sessionId False lift $ pure $ addCookie sessionCookie (Session{..}) - getCookies :: Request -> Cookies getCookies req = maybe [] parseCookies (List.lookup hCookie headers) @@ -68,14 +68,16 @@ getSessionId cookies = getInTheFuckingSessionShinji :: Pool Connection -> Maybe PersistentSessionId - -> Handler (Maybe PersistentSession) + -> LogT Handler (Maybe PersistentSession) getInTheFuckingSessionShinji _pool Nothing = pure Nothing getInTheFuckingSessionShinji pool (Just persistentSessionId) = do result <- runExceptT $ liftIO $ withPool pool $ getPersistentSession persistentSessionId case result of - Left _ -> throwError err500 Right Nothing -> pure Nothing Right (Just userSession) -> pure $ Just userSession + Left _ -> do + Logging.alert "Database error when retrieving persistent session" [] + throwError err500 fetchUser :: Pool Connection -> Maybe PersistentSession -> Handler (Maybe (User, PersistentSession)) fetchUser _ Nothing = pure Nothing diff --git a/src/FloraWeb/Server/Logging.hs b/src/FloraWeb/Server/Logging.hs new file mode 100644 index 00000000..953c7ac3 --- /dev/null +++ b/src/FloraWeb/Server/Logging.hs @@ -0,0 +1,39 @@ +module FloraWeb.Server.Logging + ( alert + , makeLogger + , runLog + ) where + +import Data.Aeson.Types (Pair) +import Data.Kind (Type) +import Data.Text (Text) +import qualified Data.Time as Time +import Log (LogT, Logger, defaultLogLevel, object, (.=)) +import qualified Log +import qualified Log.Backend.StandardOutput as Log + +import Control.Monad.Base (MonadBase) +import Control.Monad.IO.Class (MonadIO, liftIO) +import Data.Text.Display (display) +import Flora.Environment +import Optics.Core ((^.)) +-- | Wrapper around 'Log.runLogT' with necessary metadata +runLog :: forall (m :: Type -> Type) a. + FloraEnv + -> Logger + -> LogT m a + -> m a +runLog env logger logAction = + Log.runLogT ("flora-" <> suffix) logger defaultLogLevel logAction + where + suffix = display $ env ^. #environment + +makeLogger :: LoggingDestination -> (Logger -> IO a) -> IO a +makeLogger StdOut = Log.withStdOutLogger +makeLogger Json = Log.withJsonStdOutLogger + +alert :: (MonadIO m, MonadBase IO m) => Text -> [Pair] -> LogT m () +alert message details = do + timestamp <- liftIO Time.getCurrentTime + let metadata = object $ ("timestamp" .= timestamp) : details + Log.logAttention message metadata diff --git a/src/FloraWeb/Server/Logging/Metrics.hs b/src/FloraWeb/Server/Metrics.hs similarity index 98% rename from src/FloraWeb/Server/Logging/Metrics.hs rename to src/FloraWeb/Server/Metrics.hs index 90306231..90de0f3a 100644 --- a/src/FloraWeb/Server/Logging/Metrics.hs +++ b/src/FloraWeb/Server/Metrics.hs @@ -1,4 +1,4 @@ -module FloraWeb.Server.Logging.Metrics +module FloraWeb.Server.Metrics ( prometheusMiddleware ) where diff --git a/src/FloraWeb/Server/Logging/Tracing.hs b/src/FloraWeb/Server/Tracing.hs similarity index 97% rename from src/FloraWeb/Server/Logging/Tracing.hs rename to src/FloraWeb/Server/Tracing.hs index c18917b7..f4c27e1e 100644 --- a/src/FloraWeb/Server/Logging/Tracing.hs +++ b/src/FloraWeb/Server/Tracing.hs @@ -1,4 +1,4 @@ -module FloraWeb.Server.Logging.Tracing where +module FloraWeb.Server.Tracing where import Control.Exception (SomeException) import Data.ByteString.Char8 (unpack)