Skip to content

Commit

Permalink
✍️ 📥 initial backend logging support (#28)
Browse files Browse the repository at this point in the history
minimal poc linux only server logging

* parent f6712ed
author Chris McArthur <[email protected]> 1606357445 -0500
committer Chris McArthur <[email protected]> 1607133653 -0500

refactor how server header is populated

now it generated in one place

little fancy on the cmake with private features, requires C++ 17 compiler, conanfile needs conan-io/conan#8002

⚠️ do not update lockfile!

add version and settings when making locks

avoiding funky namespace declaraiton error

* adding cmake option to configure logging method

* fail bad logging options

* add logging option to conan file

* make sure to pass option list correctly

* initial pass for user databse logging

* fixing database logger

* [refactor] logger to be more generic

* add missing keyword for cmake

* adding logging to the main handlers

* build debug console logging for quick testing!

* fix bad value
  • Loading branch information
prince-chrismc authored Dec 11, 2020
1 parent f6712ed commit b341fe6
Show file tree
Hide file tree
Showing 29 changed files with 392 additions and 138 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,33 @@ jobs:
name: user_database_app
path: backend/build/src/user_database_app

backend-debug:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: ~/.conan/data
key: ${{ runner.os }}-${{ hashFiles('**/conan.lock') }}
- uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
- uses: actions/setup-python@v2
with:
python-version: 3.8
- uses: ./.github/actions/setup-conan
- uses: lukka/get-cmake@latest
- run: |
mkdir backend/build
cd backend/build
cmake .. -DCMAKE_BUILD_TYPE=Debug -DLOGGING:STRING=console
cmake --build .
- uses: actions/upload-artifact@v2
with:
name: user_database_app
path: backend/build/src/user_database_app

frontend:
runs-on: ubuntu-latest
steps:
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/upload.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,16 @@ jobs:
cd backend
conan remote add $CONAN_REMOTE $CONAN_REMOTE_URL
conan user -p ${{ secrets.JFROG_RTFACT_PASSWORD }} -r $CONAN_REMOTE prince-chrismc
conan lock create conanfile.py --version $BUILD_VERSION --lockfile=conan.lock --lockfile-out=locks/release.lock -s build_type=Release -s compiler.libcxx=libstdc++11
conan create conanfile.py $BUILD_VERSION@ --lockfile=locks/release.lock
conan lock create conanfile.py --version $BUILD_VERSION --lockfile=conan.lock --lockfile-out=locks/debug.lock -s build_type=Debug -s compiler.libcxx=libstdc++11
conan create conanfile.py $BUILD_VERSION@ --lockfile=locks/debug.lock
conan lock create conanfile.py --version $BUILD_VERSION --lockfile=conan.lock --lockfile-out=locks/console.lock -s build_type=Debug -s compiler.libcxx=libstdc++11 -o user-management:logging=console
conan create conanfile.py $BUILD_VERSION@ --lockfile=locks/console.lock
conan upload $NAME/$BUILD_VERSION@ -r $CONAN_REMOTE --all
frontend:
Expand Down
26 changes: 16 additions & 10 deletions backend/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
cmake_minimum_required(VERSION 3.8)
cmake_minimum_required(VERSION 3.8) # using cxx_std_17

project(user-management)

set(SCHEMAS_ROOT ".." CACHE STRING "root directoy where to find the JSON schemas")
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_BINARY_DIR})

include("options")

set(CONSOLE "console")
set(SYSLOG "syslog")
set(LOGGING_OPTIONS ${CONSOLE} ${SYSLOG})

options(LOGGING ${SYSLOG} "the desired logging mechanism to use" "${LOGGING_OPTIONS}")
set(SCHEMAS_ROOT ".." CACHE STRING "root directoy where to find the JSON schemas (absolute or relative path)")
option(CONAN_SETUP "use conan to setup all the dependencies" ON)
option(BUILD_WEB_SERVER "build the web server for this application" ON)
option(BUILD_TESTS "build the tests for this application" OFF)
option(RUN_TIDY "run clang-tidy" OFF)
option(COVERAGE "Enable code coverage testing" OFF)

if(${COVERAGE})
if(COVERAGE)
cmake_policy(SET CMP0079 NEW)
endif()

list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_BINARY_DIR})

if(${CONAN_SETUP})
if(CONAN_SETUP)
include("conan-setup")
endif()

Expand All @@ -30,13 +37,12 @@ pack_schemas("${SCHEMA_LIST}" schemas)

add_library(user_management "include/um/user_management.hpp")
add_library(user_management::user_management ALIAS user_management)

set_target_properties(user_management PROPERTIES LINKER_LANGUAGE CXX)
target_compile_features(user_management PUBLIC cxx_std_14)
target_include_directories(user_management PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(user_management PUBLIC schemas nlohmann_json_schema_validator::nlohmann_json_schema_validator)

if(${RUN_TIDY})
if(RUN_TIDY)
include("clang-tidy")
setup_clang_tidy(user_management)

Expand All @@ -47,10 +53,10 @@ if(${RUN_TIDY})
setup_cmake_format(main "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt")
endif()

if(${BUILD_WEB_SERVER})
if(BUILD_WEB_SERVER)
add_subdirectory(src)
endif()

if(${BUILD_TESTS})
if(BUILD_TESTS)
add_subdirectory(test)
endif()
8 changes: 8 additions & 0 deletions backend/cmake/options.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
function(OPTIONS NAME VALUE DESC CHOICES)
set(${NAME} ${VALUE} CACHE STRING "${DESC}")
set_property(CACHE ${NAME} PROPERTY STRINGS ${CHOICES})

if(NOT ${NAME} IN_LIST CHOICES)
message(FATAL_ERROR "${NAME}=${${NAME}} must be one of ${CHOICES}!")
endif()
endfunction()
12 changes: 10 additions & 2 deletions backend/conan.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
"requires": [
"1",
"10",
"12"
"12",
"13"
],
"build_requires": [
"13"
"14"
],
"path": "conanfile.py",
"context": "host"
Expand Down Expand Up @@ -76,6 +77,13 @@
"context": "host"
},
"13": {
"ref": "spdlog/1.8.1#bc83c6710733bf9bee8c664389158565",
"requires": [
"3"
],
"context": "host"
},
"14": {
"ref": "catch2/2.13.3#a4f1ed8fa355ba5a76c422fdfe94724d",
"context": "host"
}
Expand Down
5 changes: 4 additions & 1 deletion backend/conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ class UserManagementConanFile(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "cmake_find_package"
exports_sources = "CMakeLists.txt", "cmake/*", "src/*", "include/*"
default_options = {"restinio:with_openssl": True}
options = {"logging": ["console", "syslog"]}
default_options = {"logging": "syslog", "restinio:with_openssl": True}

def export_sources(self):
schema_source = path.normpath(path.join(getcwd(), ".."))
Expand All @@ -23,11 +24,13 @@ def requirements(self):
self.requires("restinio/0.6.12")
self.requires("json-schema-validator/2.1.0")
self.requires("lyra/1.5.0")
self.requires("spdlog/1.8.1")

def build(self):
cmake = CMake(self)
cmake.definitions["CONAN_SETUP"] = False
cmake.definitions["SCHEMAS_ROOT"] = "."
cmake.definitions["LOGGING"] = self.options.logging
cmake.configure()
cmake.build()

Expand Down
2 changes: 2 additions & 0 deletions backend/include/um/user_management.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include "schemas.hpp"

namespace user_management {
constexpr auto version = "1.0.0-dev.0";

using json = api::json;
using user_key = size_t;
struct user {
Expand Down
12 changes: 4 additions & 8 deletions backend/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
add_subdirectory("logging") # Needs to be first
add_subdirectory("database")
add_subdirectory("handlers")
add_subdirectory("utility")

add_executable(user_database_app "main.cpp")
target_link_libraries(user_database_app user_routes serve_files web_app app_args)
target_link_libraries(user_database_app PRIVATE user_routes serve_files web_app app_args server_logger)

if(${RUN_TIDY})
include("clang-tidy")
if(RUN_TIDY)
setup_clang_tidy(user_database_app)

include("clang-format")
setup_clang_format(user_database_app)

include("cmake-format")
setup_cmake_format(src "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt")
setup_cmake_format(src ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt)
endif()
11 changes: 3 additions & 8 deletions backend/src/database/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,10 @@ add_subdirectory("encoders")
add_library(users_databse "users.hpp" "users.cpp")
target_include_directories(users_databse PUBLIC "..")
set_target_properties(users_databse PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(users_databse PUBLIC user_management encoders fmt::fmt)
target_link_libraries(users_databse PUBLIC user_management PRIVATE logger encoders fmt::fmt)

if(${RUN_TIDY})
include("clang-tidy")
if(RUN_TIDY)
setup_clang_tidy(users_databse)

include("clang-format")
setup_clang_format(users_databse)

include("cmake-format")
setup_cmake_format(users_databse "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt")
setup_cmake_format(users_databse ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt)
endif()
13 changes: 4 additions & 9 deletions backend/src/database/encoders/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
find_package(OpenSSL REQUIRED)
find_package(OpenSSL 1.1.1 REQUIRED)

add_library(encoders "sha256.hpp" "sha256.cpp" "base64.hpp" "base64.cpp")
target_include_directories(encoders PUBLIC "..")
set_target_properties(encoders PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(encoders PUBLIC OpenSSL::OpenSSL)
target_link_libraries(encoders PRIVATE OpenSSL::OpenSSL)

if(${RUN_TIDY})
include("clang-tidy")
if(RUN_TIDY)
setup_clang_tidy(encoders)

include("clang-format")
setup_clang_format(encoders)

include("cmake-format")
setup_cmake_format(encoders "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt")
setup_cmake_format(encoders ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt)
endif()
11 changes: 11 additions & 0 deletions backend/src/database/users.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "encoders/base64.hpp"
#include "encoders/sha256.hpp"
#include "logging/logger.hpp"

namespace {
std::string make_etag(const database::user::json& json) {
Expand All @@ -17,6 +18,10 @@ std::string make_etag(const database::user::json& json) {
} // namespace

namespace database {
static const logger log{"database"};

using raw = user::json;

user::time_point user::last_modified() const {
if (count() > 0) {
const auto last_modified = std::max_element(users_last_modified.begin(), users_last_modified.end());
Expand All @@ -33,19 +38,25 @@ user::entry& user::add(const json& json) {
database_last_modified = clock::now();
auto& user = user_management::list_modifier{*this}.add(json);
users_last_modified[user.id] = clock::now();
log.info("created new user ({:d})", user.id);
log.trace("{}", raw{user});
return user;
}
user::entry& user::edit(key id, const json& json) {
database_last_modified = clock::now();
auto& user = get(id);
user_management::user_modifier{user}.apply(json);
users_last_modified[id] = clock::now();
log.info("modified user ({:d})", id);
log.trace("{}", raw{user});
return user;
}
user::entry user::remove(key id) {
database_last_modified = clock::now();
auto user = user_management::user_list::remove(id);
users_last_modified[id] = clock::now();
log.info("removed user ({:d})", id);
log.trace("{}", raw{user});
return user;
}
} // namespace database
13 changes: 5 additions & 8 deletions backend/src/handlers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,26 @@ add_subdirectory("utility")
add_library(user_routes "user_routes.hpp" "user_routes.cpp")
target_include_directories(user_routes PUBLIC "..")
set_target_properties(user_routes PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(user_routes PUBLIC routing users_databse response_builder)
target_link_libraries(user_routes PUBLIC routing users_databse PRIVATE response_builder logger)

add_library(serve_files "serve_files.hpp" "serve_files.cpp")
target_include_directories(serve_files PUBLIC "..")
set_target_properties(serve_files PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(serve_files PUBLIC content_type routing)
target_link_libraries(serve_files PUBLIC routing PRIVATE content_type add_headers logger)

add_library(web_app "web_app.hpp" "web_app.cpp")
target_include_directories(web_app PUBLIC "..")
set_target_properties(web_app PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(web_app PUBLIC content_type routing)
target_link_libraries(web_app PUBLIC routing PRIVATE content_type add_headers)

if(${RUN_TIDY})
include("clang-tidy")
if(RUN_TIDY)
setup_clang_tidy(user_routes)
setup_clang_tidy(serve_files)
setup_clang_tidy(web_app)

include("clang-format")
setup_clang_format(user_routes)
setup_clang_format(serve_files)
setup_clang_format(web_app)

include("cmake-format")
setup_cmake_format(handlers "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt")
setup_cmake_format(handlers ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt)
endif()
30 changes: 21 additions & 9 deletions backend/src/handlers/serve_files.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,50 @@

#include "serve_files.hpp"

#include "logging/logger.hpp"
#include "utility/add_headers.hpp"
#include "utility/content_type_from_ext.hpp"

namespace handler {
namespace serve_files {

static const logger log{"serve_files"};

request_status from_disk::operator()(const request_handle& req, route_params /*params*/) {
auto path = req->header().path();
log.trace("attempting to server file '{}' from disk", path);

if (std::string::npos == path.find("..")) {
// A nice path.
const auto file_path = server_root_dir + std::string{path.data(), path.size()};
const auto file_path = server_root_dir + path.data();
const auto seperator = file_path.find_last_of('.');
const auto ext = (seperator == std::string::npos) ? "" : file_path.substr(seperator + 1);
const nonstd::string_view ext = (seperator == std::string::npos) ? "" : &file_path[seperator + 1];
const nonstd::string_view content_type = content_type_by_file_extention(ext);
log.trace("the file extention is '{}' with content type '{}'", ext, content_type);
try {
auto sf = restinio::sendfile(file_path);
auto modified_at = restinio::make_date_field_value(sf.meta().last_modified_at());

auto expires_at = restinio::make_date_field_value(std::chrono::system_clock::now() + std::chrono::hours(24 * 7));
log.trace("the file was last modified at '{}' and will expire at '{}'", modified_at, expires_at);

return req->create_response()
.append_header(restinio::http_field::server, "RESTinio")
.append_header_date_field()
log.info("ok (200) sending file '{}'", path);
return response::impl::add_generic_headers(req->create_response())
.append_header(restinio::http_field::last_modified, std::move(modified_at))
.append_header(restinio::http_field::expires, std::move(expires_at))
.append_header(restinio::http_field::content_type, content_type_by_file_extention(ext))
.append_header(restinio::http_field::content_type, content_type.data())
.set_body(std::move(sf))
.done();
} catch (const std::exception&) {
return req->create_response(restinio::status_not_found()).append_header_date_field().connection_close().done();
log.info("unable to find (404) '{}'", path);
return response::impl::add_generic_headers(req->create_response(restinio::status_not_found()))
.connection_close()
.done();
}
} else {
return req->create_response(restinio::status_forbidden()).append_header_date_field().connection_close().done();
log.warn("forbiding (403) request for '{}'", path);
return response::impl::add_generic_headers(req->create_response(restinio::status_forbidden()))
.connection_close()
.done();
}
}
} // namespace serve_files
Expand Down
Loading

0 comments on commit b341fe6

Please sign in to comment.