From 3589732e6f1c99061169d9ae8a0ab37951b40f2c Mon Sep 17 00:00:00 2001 From: Syphax bouazzouni Date: Fri, 24 Jan 2025 00:49:43 +0100 Subject: [PATCH] Feature: Add last 24hours SPARQL Query logs endpoint (#116) * fix: docker-compose app volume * fix ncbo_ontology_recommender version * add last 24 hours SPARQL queries logging * add last_n_s_query_logs endpoint * add unit tests for the query logging feature * add user query count endpoint --- Gemfile.lock | 4 +- config/environments/config.rb.sample | 6 ++- controllers/logging_controller.rb | 40 ++++++++++++++ test/controllers/test_logging_controller.rb | 59 +++++++++++++++++++++ 4 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 controllers/logging_controller.rb create mode 100644 test/controllers/test_logging_controller.rb diff --git a/Gemfile.lock b/Gemfile.lock index beed6b2d..f0708f5a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,6 @@ GIT remote: https://github.com/ontoportal-lirmm/goo.git - revision: 27300f28ca6c656c7e78af65013d88b792a6312f + revision: 5825dc1f9d0ff439b1ba9d8f78fa7bb20b1c65d0 branch: development specs: goo (0.0.2) @@ -77,7 +77,7 @@ GIT GIT remote: https://github.com/ontoportal-lirmm/sparql-client.git - revision: 4364d34e9e4c411f1dd0ea706bf052465bf0b467 + revision: d4a226e75eb4aeaaf42720eac4f23f55380a0bd3 branch: development specs: sparql-client (3.2.2) diff --git a/config/environments/config.rb.sample b/config/environments/config.rb.sample index 4e7900b7..0eabcee8 100644 --- a/config/environments/config.rb.sample +++ b/config/environments/config.rb.sample @@ -52,6 +52,10 @@ LinkedData.config do |config| config.repository_folder = REPOSITORY_FOLDER.to_s # config.enable_notifications = false + # SPARQL logging + config.log_file = './sparql.log' + config.logging = false + config.interportal_hash = { "agroportal" => { "api" => "http://data.agroportal.lirmm.fr", @@ -138,4 +142,4 @@ NcboCron.config do |config| config.redis_host = REDIS_PERSISTENT_HOST.to_s config.redis_port = REDIS_PORT.to_i config.ontology_report_path = REPORT_PATH -end \ No newline at end of file +end diff --git a/controllers/logging_controller.rb b/controllers/logging_controller.rb new file mode 100644 index 00000000..09ee2347 --- /dev/null +++ b/controllers/logging_controller.rb @@ -0,0 +1,40 @@ +require 'multi_json' + +module Admin + + class LoggingController < ApplicationController + + namespace "/admin" do + before { + if LinkedData.settings.enable_security && (!env["REMOTE_USER"] || !env["REMOTE_USER"].admin?) + error 403, "Access denied" + end + } + + get '/latest_day_query_logs' do + logs = Goo.logger.get_logs + reply 200, paginate_logs(logs) + end + + get '/last_n_s_query_logs' do + sec = params[:seconds] || 10 + logs = Goo.logger.queries_last_n_seconds(sec.to_i) + reply 200, paginate_logs(logs) + end + + get '/user_query_count' do + counts = Goo.logger.users_query_count + reply 200, counts + end + + def paginate_logs(logs) + page, size = page_params + start = (page - 1) * size + page_end = [start + size - 1, logs.size].min + page_logs = logs[start..page_end] || [] + page_object(page_logs, logs.size) + end + + end + end +end diff --git a/test/controllers/test_logging_controller.rb b/test/controllers/test_logging_controller.rb new file mode 100644 index 00000000..34162145 --- /dev/null +++ b/test/controllers/test_logging_controller.rb @@ -0,0 +1,59 @@ +require_relative '../test_case' +require "multi_json" + +class TestLoggingController < TestCase + + def setup + Goo.use_cache = true + Goo.redis_client.flushdb + Goo.add_query_logger(enabled: true, file: "./queries.log") + end + + def teardown + Goo.add_query_logger(enabled: false, file: nil) + File.delete("./queries.log") if File.exist?("./queries.log") + Goo.redis_client.flushdb + Goo.use_cache = false + end + + def test_logging_endpoint + (1..10).each do |_i| + LinkedData::Models::Ontology.where.include(:acronym).all + end + + get '/admin/latest_day_query_logs?page=1&pagesize=9' + assert last_response.ok? + logs = MultiJson.load(last_response.body) + assert_equal 9, logs['collection'].size + + get '/admin/latest_day_query_logs?page=2&pagesize=9' + assert last_response.ok? + logs = MultiJson.load(last_response.body) + refute_empty logs['collection'] + + get '/admin/latest_day_query_logs?page=3&pagesize=9' + assert last_response.ok? + logs = MultiJson.load(last_response.body) + assert_empty logs['collection'] + end + + def test_n_last_seconds_logs + Goo.logger.info("Test log") + (1..10).each do |_i| + LinkedData::Models::Ontology.where.include(:acronym).all + end + + Goo.logger.info("Test log") + get '/admin/last_n_s_query_logs?seconds=2&page=1&pagesize=10' + assert last_response.ok? + logs = MultiJson.load(last_response.body) + assert_equal 10, logs['collection'].size + + sleep 1 + LinkedData::Models::Ontology.where.include(:acronym).all + get '/admin/last_n_s_query_logs?seconds=1&page=1&pagesize=10' + assert last_response.ok? + logs = MultiJson.load(last_response.body) + assert_equal 1, logs['collection'].size + end +end